void IOKitEventPublisher::stop() { if (run_loop_ == nullptr) { // If there is no run loop then the publisher thread has not started. return; } // Stop the run loop. WriteLock lock(mutex_); CFRunLoopStop(run_loop_); // Stop the run loop before operating on containers. // Destroy the IOPort. if (port_ != nullptr) { auto source = IONotificationPortGetRunLoopSource(port_); if (CFRunLoopContainsSource(run_loop_, source, kCFRunLoopDefaultMode)) { CFRunLoopRemoveSource(run_loop_, source, kCFRunLoopDefaultMode); } // And destroy the port. IONotificationPortDestroy(port_); port_ = nullptr; } // Clear all devices and their notifications. for (const auto& device : devices_) { IOObjectRelease(device->notification); } devices_.clear(); }
void CheckRunLoopSource(int deviceIndex,char *caller,int line){ CFRunLoopSourceRef currentSource; pRecDevice device; IOHIDDeviceInterface122** interface; device=PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex); if(!HIDIsValidDevice(device))PrintfExit("PsychHID: Invalid device.\n"); interface=device->interface; if(interface==NULL)PrintfExit("PsychHID: No interface for device.\n"); currentSource=(*interface)->getAsyncEventSource(interface); if(source[deviceIndex] != currentSource)printf("%s (%d): source[%d] %4.4lx != current source %4.4lx.\n" ,caller,line,(int)deviceIndex,(unsigned long)source[deviceIndex],(unsigned long)currentSource); if(ready[deviceIndex] && (source[deviceIndex]!=NULL) && !CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode)) printf("%d: %s(%d): \"ready\" but source not in CFRunLoop.\n",(int)deviceIndex,caller,line); if(!ready[deviceIndex] && (source[deviceIndex]!=NULL) && CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode)) printf("%d: %s(%d): \"!ready\" yet source is in CFRunLoop.\n",(int)deviceIndex,caller,line); }
/* 1. one must call CFRunLoopSourceInvalidate to kill the callback established by setInterruptReportHandlerCallback before calling HIDReleaseDeviceList. 2. after calling setInterruptReportHandlerCallback to enable the callback and CFRunLoopSourceInvalidate to disable it, it is then impossible to re-enable the callback with that source. To later re-enable, simply remove the source, instead of invalidating it. Once i've called CFRunLoopSourceInvalidate it appears that my only option is to release the interface by calling HIDReleaseDeviceList and start over. */ PsychError PsychHIDReceiveReportsCleanup(void) { // On a hunch, we now do both: remove the source and invalidate it. Alas, it makes no difference. I still get the CLEAR MEX crash. int deviceIndex; //printf("Clean up before PsychHID is flushed.\n"); for(deviceIndex=0;deviceIndex<MAXDEVICEINDEXS;deviceIndex++) if(source[deviceIndex]!=NULL) { CheckRunLoopSource(deviceIndex,"PsychHIDReceiveReportsCleanup",__LINE__); CFRunLoopRemoveSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode);// kCFRunLoopDefaultMode if(0 && optionsPrintCrashers)printf("%d: source %4.4lx validity %d, CFRunLoopContainsSource is %d.\n",deviceIndex,(unsigned long)source[deviceIndex] ,CFRunLoopSourceIsValid(source[deviceIndex]) ,CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode)); if(optionsPrintCrashers)printf("%d: CFRunLoopSourceInvalidate\n",deviceIndex); CFRunLoopSourceInvalidate(source[deviceIndex]); if(optionsPrintCrashers)printf("%d: source %4.4lx validity %d, CFRunLoopContainsSource is %d.\n",deviceIndex,(unsigned long)source[deviceIndex] ,CFRunLoopSourceIsValid(source[deviceIndex]) ,CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode)); ready[deviceIndex]=0; CheckRunLoopSource(deviceIndex,"PsychHIDReceiveReportsCleanup",__LINE__); source[deviceIndex]=NULL; } return 0; }
PsychError ReceiveReportsStop(int deviceIndex) { CheckRunLoopSource(deviceIndex,"ReceiveReportsStop",__LINE__); if(ready[deviceIndex]){ // Rob Yepez, at Apple, suggested that it might be better to call CFRunLoopRemoveSource than CFRunLoopSourceInvalidate. // He's right. There's no problem in re-enabling the callback after using CFRunLoopRemoveSource. // The source remains valid and can be added again to the run loop. Don't create it again. if(myRunLoopMode==NULL)myRunLoopMode=CFSTR("myMode");; // kCFRunLoopDefaultMode if(0 && optionsPrintCrashers)printf("%d: CFRunLoopRemoveSource\n",deviceIndex); CFRunLoopRemoveSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode);// kCFRunLoopDefaultMode if(0 && optionsPrintCrashers)printf("%d: source %4.4lx validity %d, CFRunLoopContainsSource is %d.\n",deviceIndex,(unsigned long)source[deviceIndex] ,CFRunLoopSourceIsValid(source[deviceIndex]) ,CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode)); ready[deviceIndex]=0; } CheckRunLoopSource(deviceIndex,"ReceiveReportsStop",__LINE__); return 0; }
/* PsychHIDReceiveReportsCleanup(void) -- Called at PsychHID shutdown time: 1. one must call CFRunLoopSourceInvalidate to kill the callback established by setInterruptReportHandlerCallback before calling HIDReleaseDeviceList. 2. after calling setInterruptReportHandlerCallback to enable the callback and CFRunLoopSourceInvalidate to disable it, it is then impossible to re-enable the callback with that source. To later re-enable, simply remove the source, instead of invalidating it. Once i've called CFRunLoopSourceInvalidate it appears that my only option is to release the interface by calling HIDReleaseDeviceList and start over. */ PsychError PsychHIDReceiveReportsCleanup(void) { int deviceIndex; //printf("Clean up before PsychHID is flushed.\n"); for(deviceIndex=0;deviceIndex<MAXDEVICEINDEXS;deviceIndex++) if(source[deviceIndex]!=NULL) { CheckRunLoopSource(deviceIndex,"PsychHIDReceiveReportsCleanup",__LINE__); CFRunLoopRemoveSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode);// kCFRunLoopDefaultMode if (optionsPrintCrashers) printf("%d: CFRunLoopSourceInvalidate\n",deviceIndex); CFRunLoopSourceInvalidate(source[deviceIndex]); if(optionsPrintCrashers) printf("%d: source %4.4lx validity %d, CFRunLoopContainsSource is %d.\n",deviceIndex,(unsigned long)source[deviceIndex] ,CFRunLoopSourceIsValid(source[deviceIndex]) ,CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode)); ready[deviceIndex]=0; CheckRunLoopSource(deviceIndex,"PsychHIDReceiveReportsCleanup",__LINE__); source[deviceIndex]=NULL; } // Release all report linked lists, memory buffers etc.: PsychHIDReleaseAllReportMemory(); return 0; }
SInt32 CFMessagePortSendRequest(CFMessagePortRef remote, SInt32 msgid, CFDataRef data, CFTimeInterval sendTimeout, CFTimeInterval rcvTimeout, CFStringRef replyMode, CFDataRef *returnDatap) { struct __CFMessagePortMachMessage *sendmsg; CFRunLoopRef currentRL = CFRunLoopGetCurrent(); CFRunLoopSourceRef source = NULL; CFDataRef reply = NULL; int64_t termTSR; uint32_t sendOpts = 0, sendTimeOut = 0; int32_t desiredReply; Boolean didRegister = false; kern_return_t ret; //#warning CF: This should be an assert // if (!__CFMessagePortIsRemote(remote)) return -999; if (!__CFMessagePortIsValid(remote)) return kCFMessagePortIsInvalid; __CFMessagePortLock(remote); if (NULL == remote->_replyPort) { CFMachPortContext context; context.version = 0; context.info = remote; context.retain = (const void *(*)(const void *))CFRetain; context.release = (void (*)(const void *))CFRelease; context.copyDescription = (CFStringRef (*)(const void *))__CFMessagePortCopyDescription; remote->_replyPort = CFMachPortCreate(CFGetAllocator(remote), __CFMessagePortReplyCallBack, &context, NULL); } remote->_convCounter++; desiredReply = -remote->_convCounter; CFDictionarySetValue(remote->_replies, (void *)desiredReply, NULL); sendmsg = __CFMessagePortCreateMessage(CFGetAllocator(remote), false, CFMachPortGetPort(remote->_port), (replyMode != NULL ? CFMachPortGetPort(remote->_replyPort) : MACH_PORT_NULL), -desiredReply, msgid, (data ? CFDataGetBytePtr(data) : NULL), (data ? CFDataGetLength(data) : 0)); __CFMessagePortUnlock(remote); if (replyMode != NULL) { source = CFMachPortCreateRunLoopSource(CFGetAllocator(remote), remote->_replyPort, -100); didRegister = !CFRunLoopContainsSource(currentRL, source, replyMode); if (didRegister) { CFRunLoopAddSource(currentRL, source, replyMode); } } if (sendTimeout < 10.0*86400) { // anything more than 10 days is no timeout! sendOpts = MACH_SEND_TIMEOUT; sendTimeout *= 1000.0; if (sendTimeout < 1.0) sendTimeout = 0.0; sendTimeOut = floor(sendTimeout); } ret = mach_msg((mach_msg_header_t *)sendmsg, MACH_SEND_MSG|sendOpts, sendmsg->head.msgh_size, 0, MACH_PORT_NULL, sendTimeOut, MACH_PORT_NULL); CFAllocatorDeallocate(CFGetAllocator(remote), sendmsg); if (KERN_SUCCESS != ret) { if (didRegister) { CFRunLoopRemoveSource(currentRL, source, replyMode); CFRelease(source); } if (MACH_SEND_TIMED_OUT == ret) return kCFMessagePortSendTimeout; return kCFMessagePortTransportError; } if (replyMode == NULL) { return kCFMessagePortSuccess; } CFRetain(remote); // retain during run loop to avoid invalidation causing freeing _CFMachPortInstallNotifyPort(currentRL, replyMode); termTSR = mach_absolute_time() + __CFTimeIntervalToTSR(rcvTimeout); for (;;) { CFRunLoopRunInMode(replyMode, __CFTSRToTimeInterval(termTSR - mach_absolute_time()), true); // warning: what, if anything, should be done if remote is now invalid? reply = CFDictionaryGetValue(remote->_replies, (void *)desiredReply); if (NULL != reply || termTSR < (int64_t)mach_absolute_time()) { break; } if (!CFMessagePortIsValid(remote)) { // no reason that reply port alone should go invalid so we don't check for that break; } } // Should we uninstall the notify port? A complex question... if (didRegister) { CFRunLoopRemoveSource(currentRL, source, replyMode); CFRelease(source); } if (NULL == reply) { CFDictionaryRemoveValue(remote->_replies, (void *)desiredReply); CFRelease(remote); return CFMessagePortIsValid(remote) ? kCFMessagePortReceiveTimeout : -5; } if (NULL != returnDatap) { *returnDatap = ((void *)0xffffffff == reply) ? NULL : reply; } else if ((void *)0xffffffff != reply) { CFRelease(reply); } CFDictionaryRemoveValue(remote->_replies, (void *)desiredReply); CFRelease(remote); return kCFMessagePortSuccess; }
PsychError ReceiveReports(int deviceIndex) { long error=0; pRecDevice device; IOHIDDeviceInterface122** interface=NULL; int reason; // kCFRunLoopRunFinished, kCFRunLoopRunStopped, kCFRunLoopRunTimedOut, kCFRunLoopRunHandledSource CountReports("ReceiveReports beginning."); if(freeReportsPtr==NULL)PrintfExit("No free reports."); PsychHIDVerifyInit(); device=PsychHIDGetDeviceRecordPtrFromIndex(deviceIndex); if(!HIDIsValidDevice(device))PrintfExit("PsychHID: Invalid device.\n"); interface=device->interface; if(interface==NULL)PrintfExit("PsychHID: No interface for device.\n"); if(deviceIndex<0 || deviceIndex>MAXDEVICEINDEXS-1)PrintfExit("Sorry. Can't cope with deviceNumber %d (more than %d). Please tell [email protected]",deviceIndex,(int)MAXDEVICEINDEXS-1); CheckRunLoopSource(deviceIndex,"ReceiveReports",__LINE__); if(!ready[deviceIndex]){ // setInterruptReportHandlerCallback static unsigned char buffer[MAXREPORTSIZE]; psych_uint32 bufferSize=MAXREPORTSIZE; psych_bool createSource; createSource=(source[deviceIndex]==NULL); if(createSource){ if(optionsPrintCrashers && createSource)printf("%d: createAsyncEventSource\n",deviceIndex); error=(*interface)->createAsyncEventSource(interface,&(source[deviceIndex])); if(error)PrintfExit("ReceiveReports - createAsyncEventSource error 0x%lx.",error); if(0 && optionsPrintCrashers && createSource) printf("%d: source %4.4lx validity %d, CFRunLoopContainsSource is %d.\n",deviceIndex,(unsigned long)source[deviceIndex] ,CFRunLoopSourceIsValid(source[deviceIndex]) ,CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode)); } if(optionsPrintCrashers && createSource)printf("%d: getAsyncEventSource\n",deviceIndex); CheckRunLoopSource(deviceIndex,"ReceiveReports",__LINE__); if(optionsPrintCrashers && createSource)printf("%d: CFRunLoopAddSource\n",deviceIndex); CFRunLoopAddSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode); if(0 && optionsPrintCrashers && createSource)printf("%d: source %4.4lx validity %d, CFRunLoopContainsSource is %d.\n",deviceIndex,(unsigned long)source[deviceIndex] ,CFRunLoopSourceIsValid(source[deviceIndex]) ,CFRunLoopContainsSource(CFRunLoopGetCurrent(),source[deviceIndex],myRunLoopMode)); ready[deviceIndex]=1; CheckRunLoopSource(deviceIndex,"ReceiveReports",__LINE__); if(optionsPrintCrashers && createSource)printf("%d: setInterruptReportHandlerCallback\n",deviceIndex); error=(*interface)->setInterruptReportHandlerCallback(interface,buffer,bufferSize,ReportCallback,buffer,(void *)deviceIndex); if(error)PrintfExit("ReceiveReports - setInterruptReportHandlerCallback error 0x%lx.",error); if(optionsPrintCrashers && createSource)printf("%d: CFRunLoopRunInMode.\n",deviceIndex); } //printf("%d: CFRunLoopRunInMode\n",deviceIndex); reason=CFRunLoopRunInMode(myRunLoopMode,optionsSecs,false); if(reason!=kCFRunLoopRunTimedOut && reason!=kCFRunLoopRunHandledSource){ char *s; switch(reason){ case kCFRunLoopRunFinished: s="kCFRunLoopRunFinished"; break; case kCFRunLoopRunStopped: s="kCFRunLoopRunStopped"; break; case kCFRunLoopRunTimedOut: s="kCFRunLoopRunTimedOut"; break; case kCFRunLoopRunHandledSource: s="kCFRunLoopRunHandledSource"; break; default: s="of unknown reason."; } printf("RunLoop ended at %.3f s because %s.\n",CFAbsoluteTimeGetCurrent()-AInScanStart,s); } CountReports("ReceiveReports end."); return error; }
CF_EXPORT rwsched_CFRunLoopSourceRef rwsched_tasklet_CFRunLoopRunTaskletBlock(rwsched_tasklet_ptr_t sched_tasklet, rwsched_CFRunLoopSourceRef blocking_source, CFTimeInterval secondsToWait) { // Validate input paraemters RW_CF_TYPE_VALIDATE(sched_tasklet, rwsched_tasklet_ptr_t); rwsched_instance_ptr_t instance = sched_tasklet->instance; RW_CF_TYPE_VALIDATE(instance, rwsched_instance_ptr_t); SInt32 runloop_status; rwsched_CFRunLoopSourceRef return_source = NULL; //int i = 0; // Validate input parameters RW_CF_TYPE_VALIDATE(instance, rwsched_instance_ptr_t); // Make sure this tasklet is not already blocked RW_ASSERT(!sched_tasklet->blocking_mode.blocked); // Mark the tasklet as blocked RW_ZERO_VARIABLE(&sched_tasklet->blocking_mode); sched_tasklet->blocking_mode.blocked = TRUE; sched_tasklet->blocking_mode.cfsource = blocking_source; // Run the run loop in the default mode for up to the block interval specified // The run loop will be stopped if an event is avilable for the wakeupSource runloop_status = CFRunLoopRunInMode(instance->main_cfrunloop_mode, secondsToWait, false); // If the return value is kCFRunLoopRunTimedOut then the run loop timed out // If the return value is kCFRunLoopRunStopped then an event occured on the wakeupSource // If any other return value occurs, it is considered to be an error if (runloop_status == kCFRunLoopRunTimedOut) { CFRunLoopRef rl = CFRunLoopGetCurrent(); return_source = blocking_source; if (CFRunLoopContainsSource(rl, blocking_source->cf_object, instance->main_cfrunloop_mode)) { return_source = NULL; } } else if (runloop_status == kCFRunLoopRunStopped || runloop_status == kCFRunLoopRunFinished) { return_source = blocking_source; } else { RW_CRASH(); } g_rwresource_track_handle = sched_tasklet->rwresource_track_handle; g_tasklet_info = sched_tasklet; // relocate timers back to the main-mode rwsched_cfrunloop_relocate_timers_to_main_mode(sched_tasklet); g_rwresource_track_handle = sched_tasklet->rwresource_track_handle; g_tasklet_info = sched_tasklet; // relocate sources back to the main-mode rwsched_cfrunloop_relocate_sources_to_main_mode(sched_tasklet); //runloop_status = rwsched_instance_CFRunLoopRunInMode(instance, instance->deferred_cfrunloop_mode, 0.0, false); //RW_ASSERT(runloop_status == kCFRunLoopRunFinished || runloop_status == kCFRunLoopRunTimedOut); // Mark the tasklet as unblocked RW_ZERO_VARIABLE(&sched_tasklet->blocking_mode); g_rwresource_track_handle = sched_tasklet->rwresource_track_handle; g_tasklet_info = sched_tasklet; //Deliver any events on the dispatch sources rwsched_dispatch_unblock_sources(sched_tasklet); g_rwresource_track_handle = sched_tasklet->rwresource_track_handle; g_tasklet_info = sched_tasklet; // Return the runloop source that wokeup the blocking call, which is NULL if it timed out return return_source; }
/* CF_EXPORT */ void CFNetServiceBrowserStopSearch(CFNetServiceBrowserRef b, CFStreamError* error) { __CFNetServiceBrowser* browser = (__CFNetServiceBrowser*)b; // By default, the error is marked as a cancel CFStreamError extra = {kCFStreamErrorDomainNetServices , kCFNetServicesErrorCancel}; // Make sure error has a value. if (!error) error = &extra; // Lock down the browser __CFSpinLock(&(browser->_lock)); // Make sure there is something to cancel. if (browser->_trigger) { CFRunLoopSourceContext ctxt = { 0, // version browser, // info NULL, // retain NULL, // release NULL, // copyDescription NULL, // equal NULL, // hash NULL, // schedule NULL, // cancel (void(*)(void*))(&_BrowserCancel) // perform }; // Remove the trigger from run loops and modes _CFTypeUnscheduleFromMultipleRunLoops(browser->_trigger, browser->_schedules); // Go ahead and invalidate the trigger _CFTypeInvalidate(browser->_trigger); // Release the trigger now. CFRelease(browser->_trigger); // Need to clean up the service discovery stuff if there is if (browser->_browse) { // Release the underlying service discovery reference DNSServiceRefDeallocate(browser->_browse); browser->_browse = NULL; // Dump all the lists of items. CFDictionaryRemoveAllValues(browser->_found); CFArrayRemoveAllValues(browser->_adds); CFArrayRemoveAllValues(browser->_removes); } // Copy the error into place memmove(&(browser->_error), error, sizeof(error[0])); // Create the cancel source browser->_trigger = CFRunLoopSourceCreate(CFGetAllocator(browser), 0, &ctxt); // If the cancel was created, need to schedule and signal it. if (browser->_trigger) { CFArrayRef schedules = browser->_schedules; CFIndex i, count = CFArrayGetCount(schedules); // Schedule the new trigger _CFTypeScheduleOnMultipleRunLoops(browser->_trigger, schedules); // Signal the cancel for immediate attention. CFRunLoopSourceSignal((CFRunLoopSourceRef)(browser->_trigger)); // Make sure the signal can make it through for (i = 0; i < count; i += 2) { // Grab the run loop for checking CFRunLoopRef runloop = (CFRunLoopRef)CFArrayGetValueAtIndex(schedules, i); // If it's sleeping, need to further check it. if (CFRunLoopIsWaiting(runloop)) { // Grab the mode for further check CFStringRef mode = CFRunLoopCopyCurrentMode(runloop); if (mode) { // If the trigger is in the right mode, need to wake up the run loop. if (CFRunLoopContainsSource(runloop, (CFRunLoopSourceRef)(browser->_trigger), mode)) { CFRunLoopWakeUp(runloop); } // Don't need this anymore. CFRelease(mode); } } } } } // Unlock the browser __CFSpinUnlock(&(browser->_lock)); }
//--------------------------------------------------------------------------- // wxJoystickThread::Entry // // Thread procedure // // Runs a CFRunLoop for polling. Basically, it sets the HID queue to // call wxJoystickThread::HIDCallback in the context of this thread // when something changes on the device. It polls as long as the user // wants, or a certain amount if the user wants to "block". Note that // we don't actually block here since this is in a secondary thread. //--------------------------------------------------------------------------- void* wxJoystickThread::Entry() { CFRunLoopSourceRef pRLSource = NULL; if ((*m_hid->GetQueue())->createAsyncEventSource( m_hid->GetQueue(), &pRLSource) != kIOReturnSuccess ) { wxLogSysError(wxT("Couldn't create async event source")); return NULL; } wxASSERT(pRLSource != NULL); //attach runloop source to main run loop in thread CFRunLoopRef pRL = CFRunLoopGetCurrent(); CFRunLoopAddSource(pRL, pRLSource, kCFRunLoopDefaultMode); wxASSERT( CFRunLoopContainsSource(pRL, pRLSource, kCFRunLoopDefaultMode) ); if( (*m_hid->GetQueue())->setEventCallout(m_hid->GetQueue(), wxJoystickThread::HIDCallback, this, this) != kIOReturnSuccess ) { wxLogSysError(wxT("Could not set event callout for queue")); return NULL; } if( (*m_hid->GetQueue())->start(m_hid->GetQueue()) != kIOReturnSuccess ) { wxLogSysError(wxT("Could not start queue")); return NULL; } double dTime; while(true) { if (TestDestroy()) break; if (m_polling) dTime = 0.0001 * m_polling; else dTime = 0.0001 * 10; // check at least every 10 msec in "blocking" case //true just "handles and returns" - false forces it to stay the time //amount #if 1 CFRunLoopRunInMode(kCFRunLoopDefaultMode, dTime, true); #else IOReturn ret = NULL; HIDCallback(this, ret, this, this); Sleep(3000); #endif } wxASSERT( CFRunLoopContainsSource(pRL, pRLSource, kCFRunLoopDefaultMode) ); CFRunLoopRemoveSource(pRL, pRLSource, kCFRunLoopDefaultMode); CFRelease(pRLSource); return NULL; }