Skip to content

Instantly share code, notes, and snippets.

@ArnaudGallardo
Created August 3, 2018 14:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ArnaudGallardo/b67dadfa481c2a9077e6a0b363ec2b7b to your computer and use it in GitHub Desktop.
Save ArnaudGallardo/b67dadfa481c2a9077e6a0b363ec2b7b to your computer and use it in GitHub Desktop.
Simple reproducer - LeapPollConnection deadlock
#include "LeapInterface.h"
#include <iostream>
#include <mutex>
#include <chrono>
#include <string>
static std::mutex g_dataLock;
static LEAP_TRACKING_EVENT* g_lastFrame = NULL;
static LEAP_DEVICE_INFO* g_lastDevice = NULL;
static const char*
ResultString( eLeapRS r ) {
switch (r){
case eLeapRS_Success: return "eLeapRS_Success";
case eLeapRS_UnknownError: return "eLeapRS_UnknownError";
case eLeapRS_InvalidArgument: return "eLeapRS_InvalidArgument";
case eLeapRS_InsufficientResources: return "eLeapRS_InsufficientResources";
case eLeapRS_InsufficientBuffer: return "eLeapRS_InsufficientBuffer";
case eLeapRS_Timeout: return "eLeapRS_Timeout";
case eLeapRS_NotConnected: return "eLeapRS_NotConnected";
case eLeapRS_HandshakeIncomplete: return "eLeapRS_HandshakeIncomplete";
case eLeapRS_BufferSizeOverflow: return "eLeapRS_BufferSizeOverflow";
case eLeapRS_ProtocolError: return "eLeapRS_ProtocolError";
case eLeapRS_InvalidClientID: return "eLeapRS_InvalidClientID";
case eLeapRS_UnexpectedClosed: return "eLeapRS_UnexpectedClosed";
case eLeapRS_UnknownImageFrameRequest: return "eLeapRS_UnknownImageFrameRequest";
case eLeapRS_UnknownTrackingFrameID: return "eLeapRS_UnknownTrackingFrameID";
case eLeapRS_RoutineIsNotSeer: return "eLeapRS_RoutineIsNotSeer";
case eLeapRS_TimestampTooEarly: return "eLeapRS_TimestampTooEarly";
case eLeapRS_ConcurrentPoll: return "eLeapRS_ConcurrentPoll";
case eLeapRS_NotAvailable: return "eLeapRS_NotAvailable";
case eLeapRS_NotStreaming: return "eLeapRS_NotStreaming";
case eLeapRS_CannotOpenDevice: return "eLeapRS_CannotOpenDevice";
default: return "unknown result type.";
}
}
/**
* Set the cached tracking frame
*/
static void
setFrame( const LEAP_TRACKING_EVENT *frame ){
g_dataLock.lock();
if (!g_lastFrame) g_lastFrame = (LEAP_TRACKING_EVENT*)malloc( sizeof( *frame ) );
*g_lastFrame = *frame;
g_dataLock.unlock();
}
/**
* Caches the last device found by copying the device info struct returned by LeapC
*/
static void
setDevice( const LEAP_DEVICE_INFO *deviceProps ){
std::cout << "setDevice" << std::endl;
g_dataLock.lock();
if (g_lastDevice){
free( g_lastDevice->serial );
}
else {
g_lastDevice = (LEAP_DEVICE_INFO*)malloc( sizeof( *deviceProps ) );
}
*g_lastDevice = *deviceProps;
g_lastDevice->serial = (char*)malloc( deviceProps->serial_length );
memcpy( g_lastDevice->serial, deviceProps->serial, deviceProps->serial_length );
g_dataLock.unlock();
}
/**
* Called by serviceMessageLoop() when a device lost event is returned by LeapPollConnection().
*/
static void
handleDeviceLostEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_DEVICE_EVENT *device_event )
{
if (connectionCallbacks.on_device_lost){
connectionCallbacks.on_device_lost();
}
}
/**
* Called by serviceMessageLoop() when a device failure event is returned by LeapPollConnection().
*/
static void
handleDeviceFailureEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_DEVICE_FAILURE_EVENT *device_failure_event )
{
if (connectionCallbacks.on_device_failure){
connectionCallbacks.on_device_failure( device_failure_event->status, device_failure_event->hDevice );
}
}
/**
* Called by serviceMessageLoop() when a tracking event is returned by LeapPollConnection().
*/
static void
handleTrackingEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_TRACKING_EVENT *tracking_event )
{
setFrame( tracking_event ); //support polling tracking data from different thread
if (connectionCallbacks.on_frame){
connectionCallbacks.on_frame( tracking_event );
}
}
/**
* Called by serviceMessageLoop() when a log event is returned by LeapPollConnection().
*/
static void
handleLogEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_LOG_EVENT *log_event )
{
if (connectionCallbacks.on_log_message){
connectionCallbacks.on_log_message( log_event->severity, log_event->timestamp, log_event->message );
}
}
/**
* Called by serviceMessageLoop() when a log event is returned by LeapPollConnection().
*/
static void
handleLogEvents( LeapInterface::Callbacks connectionCallbacks, const LEAP_LOG_EVENTS *log_events )
{
if (connectionCallbacks.on_log_message){
for (int i = 0; i < (int)(log_events->nEvents); i++) {
const LEAP_LOG_EVENT* log_event = &log_events->events[i];
connectionCallbacks.on_log_message( log_event->severity, log_event->timestamp, log_event->message );
}
}
}
/**
* Called by serviceMessageLoop() when a policy event is returned by LeapPollConnection().
*/
static void
handlePolicyEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_POLICY_EVENT *policy_event )
{
if (connectionCallbacks.on_policy){
connectionCallbacks.on_policy( policy_event->current_policy );
}
}
/**
* Called by serviceMessageLoop() when a config change event is returned by LeapPollConnection().
*/
static void
handleConfigChangeEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_CONFIG_CHANGE_EVENT *config_change_event )
{
if (connectionCallbacks.on_config_change){
connectionCallbacks.on_config_change( config_change_event->requestID, config_change_event->status );
}
}
/**
* Called by serviceMessageLoop() when a config response event is returned by LeapPollConnection().
*/
static void
handleConfigResponseEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_CONFIG_RESPONSE_EVENT *config_response_event )
{
if (connectionCallbacks.on_config_response){
connectionCallbacks.on_config_response( config_response_event->requestID, config_response_event->value );
}
}
/**
* Called by serviceMessageLoop() when a point mapping change event is returned by LeapPollConnection().
*/
static void
handleImageEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_IMAGE_EVENT *image_event )
{
if (connectionCallbacks.on_image){
connectionCallbacks.on_image( image_event );
}
}
/**
* Called by serviceMessageLoop() when a point mapping change event is returned by LeapPollConnection().
*/
static void
handlePointMappingChangeEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_POINT_MAPPING_CHANGE_EVENT *point_mapping_change_event )
{
if (connectionCallbacks.on_point_mapping_change){
connectionCallbacks.on_point_mapping_change( point_mapping_change_event );
}
}
/**
* Called by serviceMessageLoop() when a point mapping change event is returned by LeapPollConnection().
*/
static void
handleHeadPoseEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_HEAD_POSE_EVENT *head_pose_event )
{
if (connectionCallbacks.on_head_pose){
connectionCallbacks.on_head_pose( head_pose_event );
}
}
/**
* Called by serviceMessageLoop() when a connection event is returned by LeapPollConnection().
*/
static void
handleConnectionEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_CONNECTION_EVENT *connection_event ){
if (connectionCallbacks.on_connection){
connectionCallbacks.on_connection();
}
}
/**
* Called by serviceMessageLoop() when a connection lost event is returned by LeapPollConnection().
*/
static void
handleConnectionLostEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_CONNECTION_LOST_EVENT *connection_lost_event ){
if (connectionCallbacks.on_connection_lost){
connectionCallbacks.on_connection_lost();
}
}
static void
handleDeviceEvent( LeapInterface::Callbacks connectionCallbacks, const LEAP_DEVICE_EVENT *device_event ){
LEAP_DEVICE deviceHandle;
std::cout << "device" << std::endl;
//Open device using LEAP_DEVICE_REF from event struct.
eLeapRS result = LeapOpenDevice( device_event->device, &deviceHandle );
if (result != eLeapRS_Success){
const std::string message = "Could not open device " + std::string( ResultString( result ) ) + ".";
//SoDebugError::post( "handleDeviceEvent", message.c_str() );
return;
}
//Create a struct to hold the device properties, we have to provide a buffer for the serial string
LEAP_DEVICE_INFO deviceProperties = { sizeof( deviceProperties ) };
// Start with a length of 1 (pretending we don't know a priori what the length is).
// Currently device serial numbers are all the same length, but that could change in the future
deviceProperties.serial_length = 1;
deviceProperties.serial = (char*)malloc( deviceProperties.serial_length );
//This will fail since the serial buffer is only 1 character long
// But deviceProperties is updated to contain the required buffer length
result = LeapGetDeviceInfo( deviceHandle, &deviceProperties );
if (result == eLeapRS_InsufficientBuffer){
//try again with correct buffer size
deviceProperties.serial = (char*)realloc( deviceProperties.serial, deviceProperties.serial_length );
result = LeapGetDeviceInfo( deviceHandle, &deviceProperties );
if (result != eLeapRS_Success){
const std::string message = "Failed to get device info " + std::string( ResultString( result ) ) + ".";
//SoDebugError::post( "handleDeviceEvent", message.c_str() );
free( deviceProperties.serial );
return;
}
}
setDevice( &deviceProperties );
if (connectionCallbacks.on_device_found){
connectionCallbacks.on_device_found( &deviceProperties );
}
free( deviceProperties.serial );
LeapCloseDevice( deviceHandle );
}
void*
serviceMessageLoop( void* userData )
{
LeapInterface::t_userData *data = (LeapInterface::t_userData*)userData;
eLeapRS result;
LEAP_CONNECTION_MESSAGE msg;
std::cout << "Start thread : " << data->connectionHandle << std::endl;
while (data->isRunning){
unsigned int timeout = 1000;
std::cout << "loop : " << data->isConnected << std::endl;
result = LeapPollConnection( data->connectionHandle, timeout, &msg );
std::cout << "msg : " << msg.type << " " << result << std::endl;
if (result != eLeapRS_Success){
const std::string message = "LeapC PollConnection call was " + std::string( ResultString( result ) ) + ".";
//SoDebugError::post( "serviceMessageLoop", message.c_str() );
continue;
}
switch (msg.type){
case eLeapEventType_Connection:
data->isConnected = true;
handleConnectionEvent( data->connectionCallbacks, msg.connection_event );
break;
case eLeapEventType_ConnectionLost:
data->isConnected = false;
handleConnectionLostEvent( data->connectionCallbacks, msg.connection_lost_event );
break;
case eLeapEventType_Device:
handleDeviceEvent( data->connectionCallbacks, msg.device_event );
break;
case eLeapEventType_DeviceLost:
handleDeviceLostEvent( data->connectionCallbacks, msg.device_event );
break;
case eLeapEventType_DeviceFailure:
handleDeviceFailureEvent( data->connectionCallbacks, msg.device_failure_event );
break;
case eLeapEventType_Tracking:
handleTrackingEvent( data->connectionCallbacks, msg.tracking_event );
break;
case eLeapEventType_ImageComplete:
// Ignore
break;
case eLeapEventType_ImageRequestError:
// Ignore
break;
case eLeapEventType_LogEvent:
handleLogEvent( data->connectionCallbacks, msg.log_event );
break;
case eLeapEventType_Policy:
handlePolicyEvent( data->connectionCallbacks, msg.policy_event );
break;
case eLeapEventType_ConfigChange:
handleConfigChangeEvent( data->connectionCallbacks, msg.config_change_event );
break;
case eLeapEventType_ConfigResponse:
handleConfigResponseEvent( data->connectionCallbacks, msg.config_response_event );
break;
case eLeapEventType_Image:
handleImageEvent( data->connectionCallbacks, msg.image_event );
break;
case eLeapEventType_PointMappingChange:
handlePointMappingChangeEvent( data->connectionCallbacks, msg.point_mapping_change_event );
break;
case eLeapEventType_LogEvents:
handleLogEvents( data->connectionCallbacks, msg.log_events );
break;
case eLeapEventType_HeadPose:
handleHeadPoseEvent( data->connectionCallbacks, msg.head_pose_event );
break;
default:
//discard unknown message types
const std::string message = "Unhandled message type " + std::to_string( msg.type ) + ".";
//SoDebugError::postInfo( "serviceMessageLoop", message.c_str() );
break;
}
}
return NULL;
}
LeapInterface::LeapInterface()
{
m_threadData.isRunning = false;
m_threadData.isConnected = false;
m_threadData.connectionHandle = NULL;
m_threadData.connectionCallbacks.on_connection = NULL;
m_threadData.connectionCallbacks.on_connection_lost = NULL;
m_threadData.connectionCallbacks.on_device_found = NULL;
m_threadData.connectionCallbacks.on_device_lost = NULL;
m_threadData.connectionCallbacks.on_device_failure = NULL;
m_threadData.connectionCallbacks.on_policy = NULL;
m_threadData.connectionCallbacks.on_frame = NULL;
m_threadData.connectionCallbacks.on_log_message = NULL;
m_threadData.connectionCallbacks.on_config_change = NULL;
m_threadData.connectionCallbacks.on_config_response = NULL;
m_threadData.connectionCallbacks.on_image = NULL;
m_threadData.connectionCallbacks.on_point_mapping_change = NULL;
m_threadData.connectionCallbacks.on_head_pose = NULL;
}
LeapInterface::~LeapInterface()
{
this->DestroyConnection();
}
LEAP_CONNECTION*
LeapInterface::OpenConnection(){
if (m_threadData.isRunning){
return &m_threadData.connectionHandle;
}
std::cout << "test : " << m_threadData.connectionHandle << std::endl;
if (m_threadData.connectionHandle || LeapCreateConnection( NULL, &m_threadData.connectionHandle ) == eLeapRS_Success){
eLeapRS result = LeapOpenConnection( m_threadData.connectionHandle );
if (result == eLeapRS_Success){
m_threadData.isRunning = true;
std::cout << "Connection OK : " << m_threadData.connectionHandle << std::endl;
m_pollingThread = std::thread( serviceMessageLoop, (void*)&m_threadData );
}
else
{
std::cout << "error open : " << result << std::endl;
}
}
return &m_threadData.connectionHandle;
}
void
LeapInterface::CloseConnection(){
if (!m_threadData.isRunning)
return;
g_dataLock.lock();
m_threadData.isRunning = false;
g_dataLock.unlock();
m_pollingThread.join();
LeapCloseConnection( m_threadData.connectionHandle );
}
void
LeapInterface::DestroyConnection(){
CloseConnection();
LeapDestroyConnection( m_threadData.connectionHandle );
}
bool
LeapInterface::IsConnected()
{
return m_threadData.isConnected;
}
LEAP_TRACKING_EVENT*
LeapInterface::GetFrame(){
LEAP_TRACKING_EVENT *currentFrame;
g_dataLock.lock();
currentFrame = g_lastFrame;
g_dataLock.unlock();
return currentFrame;
}
LEAP_DEVICE_INFO*
LeapInterface::GetDeviceProperties(){
std::cout << "test" << std::endl;
LEAP_DEVICE_INFO *currentDevice;
g_dataLock.lock();
currentDevice = g_lastDevice;
g_dataLock.unlock();
return currentDevice;
}
void
LeapInterface::millisleep( int milliseconds ){
std::chrono::milliseconds dura( milliseconds );
std::this_thread::sleep_for( dura );
}
#ifndef LeapInterface_h
#define LeapInterface_h
#include <thread>
#include <LeapC.h>
/**
* @class LeapInterface
* @brief LeapMotion SDK interface
*
* This class is mostly inspired by the example code found on LeapMotion's website.
* https://developer.leapmotion.com/documentation/v4/examples.html
*
* Transform unit is millimeters.
*/
class LeapInterface
{
public:
/**
* Default Constructor
*/
LeapInterface();
/**
* Destructor
*/
~LeapInterface();
/**
* Initialize the connection with the LeapMotion and create the message loop thread
*/
LEAP_CONNECTION* OpenConnection();
/**
* Release the connection with the LeapMotion and stop the message loop thread
*/
void CloseConnection();
/**
* Close and destroy the connection
*/
void DestroyConnection();
/**
* Returns a pointer to the cached tracking frame
*/
LEAP_TRACKING_EVENT* GetFrame();
/**
* Returns a pointer to the cached device info
*/
LEAP_DEVICE_INFO* GetDeviceProperties();
/**
* Translates eLeapRS result codes into a human-readable string
*/
const char* ResultString( eLeapRS r );
/**
* Returns the current connection status
*/
bool IsConnected();
typedef void( *connection_callback ) ();
typedef void( *device_callback ) (const LEAP_DEVICE_INFO *device);
typedef void( *device_lost_callback ) ();
typedef void( *device_failure_callback ) (const eLeapDeviceStatus failure_code,
const LEAP_DEVICE failed_device);
typedef void( *policy_callback ) (const uint32_t current_policies);
typedef void( *tracking_callback ) (const LEAP_TRACKING_EVENT *tracking_event);
typedef void( *log_callback ) (const eLeapLogSeverity severity,
const int64_t timestamp,
const char* message);
typedef void( *config_change_callback ) (const uint32_t requestID, const bool success);
typedef void( *config_response_callback )(const uint32_t requestID, LEAP_VARIANT value);
typedef void( *image_callback ) (const LEAP_IMAGE_EVENT *image_event);
typedef void( *point_mapping_change_callback )(const LEAP_POINT_MAPPING_CHANGE_EVENT *point_mapping_change_event);
typedef void( *head_pose_callback )(const LEAP_HEAD_POSE_EVENT *head_pose_event);
struct Callbacks{
connection_callback on_connection;
connection_callback on_connection_lost;
device_callback on_device_found;
device_lost_callback on_device_lost;
device_failure_callback on_device_failure;
policy_callback on_policy;
tracking_callback on_frame;
log_callback on_log_message;
config_change_callback on_config_change;
config_response_callback on_config_response;
image_callback on_image;
point_mapping_change_callback on_point_mapping_change;
head_pose_callback on_head_pose;
};
typedef struct s_userData
{
bool isRunning;
bool isConnected;
LEAP_CONNECTION connectionHandle;
struct Callbacks connectionCallbacks;
} t_userData;
/**
* Sleep function
*/
static void millisleep( int milliseconds );
private:
std::thread m_pollingThread;
t_userData m_threadData;
};
#endif
#include <windows.h>
#include <iostream>
#include "LeapInterface.h"
#define CreateWindowA(lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)\
CreateWindowExA(0L, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)
#define CreateWindowW(lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)\
CreateWindowExW(0L, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)
#ifdef UNICODE
#define CreateWindow CreateWindowW
#else
#define CreateWindow CreateWindowA
#endif
const char g_szClassName[] = "myWindowClass";
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch (msg)
{
case WM_CLOSE:
DestroyWindow( hwnd );
break;
case WM_DESTROY:
PostQuitMessage( 0 );
break;
default:
return DefWindowProc( hwnd, msg, wParam, lParam );
}
return 0;
}
int
main( int argc, char** argv )
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
//Step 1: Registering the Window Class
wc.cbSize = sizeof( WNDCLASSEX );
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle( NULL );
wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon( NULL, IDI_APPLICATION );
if (!RegisterClassEx( &wc ))
{
MessageBox( NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK );
return 0;
}
// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"The title of my window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
NULL, NULL, wc.hInstance, NULL );
if (hwnd == NULL)
{
MessageBox( NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK );
return 0;
}
ShowWindow( hwnd, SW_SHOWNORMAL );
UpdateWindow( hwnd );
// ------------ Enable a connection with the LeapMotion -------------
LeapInterface* leapController = new LeapInterface;
LEAP_CONNECTION* connHandle = leapController->OpenConnection();
std::cout << "Connecting to Leap Motion Service..." << std::endl;
while (!leapController->IsConnected()){
LeapInterface::millisleep( 250 );
}
std::cout << "Connected!\nConnecting to device..." << std::endl;
LEAP_DEVICE_INFO* deviceProps = leapController->GetDeviceProperties();
while (!deviceProps)
{
deviceProps = leapController->GetDeviceProperties();
LeapInterface::millisleep( 250 );
}
std::cout << "Using device : " << deviceProps->serial << std::endl;
// ------------ Enable a connection with the LeapMotion -------------
// Step 3: The Message Loop
while (GetMessage( &Msg, NULL, 0, 0 ) > 0)
{
TranslateMessage( &Msg );
DispatchMessage( &Msg );
LEAP_TRACKING_EVENT *frame = leapController->GetFrame();
if (frame)
std::cout << frame->nHands << std::endl;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment