/* Count of number of times we've gotten an "Entry" notification for *ourrunloopobserver.
*/ staticint current_loop_level = 0;
/* Run loop level at which we acquired ownership of the GLib main *loop.Seenoteinrun_loop_entry().-1meansthatwedon’thave *ownership
*/ staticint acquired_loop_level = -1;
/* Between run_loop_before_waiting() and run_loop_after_waiting(); *whetherweweneedtocallselect_thread_collect_poll()
*/ static gboolean run_loop_polling_async = FALSE;
/* Between run_loop_before_waiting() and run_loop_after_waiting(); *max_prioritiytopasstog_main_loop_check()
*/ staticint run_loop_max_priority;
/* Timer that we've added to wake up the run loop when a GLib timeout
*/ static CFRunLoopTimerRef run_loop_timer = NULL;
/* These are the file descriptors that are we are polling out of *therunloop.(Wekeepthearrayaroundandreuseittoavoid *constantallocations.)
*/ #define RUN_LOOP_POLLFDS_INITIAL_SIZE 16 static GPollFD *run_loop_pollfds; static guint run_loop_pollfds_size; /* Allocated size of the array */ static guint run_loop_n_pollfds; /* Number of file descriptors in the array */
/******* Other global variables *******/
/* Since we count on replacing the GLib main loop poll function as our *methodofintegratingCocoaeventhandlingintotheGLibmainloop *weneedtomakesurethatthepollfunctionisalwayscalledeven *whentherearenofiledescriptorsthatneedtobepolled.Todo *this,weaddadummyGPollFDtooureventsourcewithafile *descriptorof“-1”.ThenanytimethatGLibispollingtheevent *source,itwillcallourpollfunction.
*/ static GPollFD event_poll_fd;
/* Current NSEvents that we've gotten from Cocoa but haven't yet converted *toGdkEvents.Wewaituntilourdispatch()functiontodotheconversion *sincetheconversioncanconceivablycausesignalstobeemitted *orotherthingsthatshouldn’thappeninsideapollfunction.
*/ static GQueue *current_events;
/* The default poll function for GLib; we replace this with our own *Cocoa-awareversionandthencalltheoldversiontodoactual *filedescriptorpolling.There’snoactualneedtochaintothe *oldone;wecouldreimplementthesamefunctionalityfromscratch, *butsincethedefaultimplementationdoestherightthing,why *bother.
*/ static GPollFunc old_poll_func;
/* Reference to the run loop of the main thread. (There is a unique *CFRunLoopperthread.)
*/ static CFRunLoopRef main_thread_run_loop;
/* Normally the Cocoa main loop maintains an NSAutoReleasePool and frees *itoneveryiteration.Sincewearereplacingthemainloopwehave *toprovidethisfunctionalityourself.Wefreeandreplacethe *auto-releasepoolinoursourcesprepare()function.
*/ static NSAutoreleasePool *autorelease_pool;
/* Flag when we've called nextEventMatchingMask ourself; this triggers *arunloopiteration,soweneedtodetectthatandavoidtriggering *our"runtheGLibmainloopwhiletherunloopisactivemachinery.
*/ staticint getting_events = 0;
/* The states in our state machine, see comments in select_thread_func() *fordescriptionsofeachstate
*/ typedefenum {
BEFORE_START,
WAITING,
POLLING_QUEUED,
POLLING_RESTART,
POLLING_DESCRIPTORS,
} SelectThreadState;
/* These are the file descriptors that the select thread is currently *polling.
*/ static GPollFD *current_pollfds; static guint current_n_pollfds;
/* These are the file descriptors that the select thread should pick *upandstartpollingwhenithasachance.
*/ static GPollFD *next_pollfds; static guint next_n_pollfds;
/* Pipe used to wake up the select thread */ staticint select_thread_wakeup_pipe[2];
/* Run loop source used to wake up the main thread */ static CFRunLoopSourceRef select_main_thread_source;
staticvoid
signal_main_thread (void)
{
GDK_DEBUG (EVENTLOOP, "EventLoop: Waking up main thread");
/* If we are in nextEventMatchingMask, then we need to make sure an *eventgetsqueued,otherwiseit'senoughtosimplywakeupthe *mainthreadrunloop
*/ if (!run_loop_polling_async)
CFRunLoopSourceSignal (select_main_thread_source);
/* Don't check for CFRunLoopIsWaiting() here because it causes a *racecondition(theloopcouldgointowaitingstaterightafter *wechecked).
*/
CFRunLoopWakeUp (main_thread_run_loop);
}
while (TRUE)
{ switch (select_thread_state)
{ case BEFORE_START: /* The select thread has not been started yet
*/
g_assert_not_reached ();
case WAITING: /* Waiting for a set of file descriptors to be submitted by the main thread * *=>POLLING_QUEUED:mainthreadthreadsubmitsasetoffiledescriptors
*/
SELECT_THREAD_WAIT (); break;
case POLLING_QUEUED: /* Waiting for a set of file descriptors to be submitted by the main thread * *=>POLLING_DESCRIPTORS:selectthreadpicksupthefiledescriptorstobeginpolling
*/
g_free (current_pollfds);
case POLLING_RESTART: /* Select thread is currently polling a set of file descriptors, main thread has *begananewiterationwiththesamesetoffiledescriptors.Wedon'twantto *waketheselectthreadupandwaitforittorestartimmediately,buttoavoid *arace(describedbelowinselect_thread_start_polling())weneedtorecheckafter *pollingcompletes. * *=>POLLING_DESCRIPTORS:selectcompletes,mainthreadrechecksbypollingagain *=>POLLING_QUEUED:mainthreadsubmitsanewsetoffiledescriptorstobepolled
*/
select_thread_set_state (POLLING_DESCRIPTORS); break;
case POLLING_DESCRIPTORS: /* In the process of polling the file descriptors * *=>WAITING:pollingcompleteswhenafiledescriptorbecomesactive *=>POLLING_QUEUED:mainthreadsubmitsanewsetoffiledescriptorstobepolled *=>POLLING_RESTART:mainthreadbeginsanewiterationwiththesamesetfiledescriptors
*/
SELECT_THREAD_UNLOCK ();
old_poll_func (current_pollfds, current_n_pollfds, -1);
SELECT_THREAD_LOCK ();
for (i = 0; i < old_n_pollfds; i++)
{ if (old_pollfds[i].fd != new_pollfds[i].fd ||
old_pollfds[i].events != new_pollfds[i].events) returnFALSE;
}
returnTRUE;
}
/* Begins a polling operation with the specified GPollFD array; the *timeoutisusedonlytotellifthepollingoperationisblocking *ornon-blocking. * *Returns: *-1:Nofiledescriptorsready,beganasynchronouspoll *0:Nofiledescriptorsready,asynchronouspollnotneeded *>0:Numberoffiledescriptorsready
*/ staticint
select_thread_start_poll (GPollFD *ufds,
guint nfds, int timeout)
{ int n_ready;
gboolean have_new_pollfds = FALSE; int poll_fd_index = -1; int i;
for (i = 0; i < nfds; i++) if (ufds[i].fd == -1)
{
poll_fd_index = i; break;
}
/* If we went immediately to an async poll, then we might decide to *dispatchidlefunctionswhenhigherpriorityfiledescriptorsources *arereadytobedispatched.Sowealwaysneedtofirstcheck *checksynchronouslywithatimeoutofzero,andonlywhenno *sourcesareimmediatelyready,gototheasynchronouspoll. * *Ofcourse,ifthetimeoutpassedinis0,thenthesynchronous *checkissufficientandweneverneedtodotheasynchronouspoll.
*/
n_ready = old_poll_func (ufds, nfds, 0); if (n_ready > 0 || timeout == 0)
{ if (GDK_DEBUG_CHECK (EVENTLOOP) && n_ready > 0)
{
gdk_debug_message ("EventLoop: Found ready file descriptors before waiting");
dump_poll_result (ufds, nfds);
}
return n_ready;
}
SELECT_THREAD_LOCK ();
if (select_thread_state == BEFORE_START)
{
select_thread_start ();
}
if (select_thread_state == POLLING_QUEUED)
{ /* If the select thread hasn't picked up the set of file descriptors yet *thenwecansimplyreplaceanoldstalesetwithanewset.
*/ if (!pollfds_equal (ufds, nfds, next_pollfds, next_n_pollfds - 1))
{
g_clear_pointer (&next_pollfds, g_free);
next_n_pollfds = 0;
have_new_pollfds = TRUE;
}
} elseif (select_thread_state == POLLING_RESTART || select_thread_state == POLLING_DESCRIPTORS)
{ /* If we are already in the process of polling the right set of file descriptors, *there'snoneedforustoimmediatelyforcetheselectthreadtostoppolling *andthenrestartagain.Andavoidingdoingsoincreasestheefficiencyconsiderably *inthecommoncasewherewehaveasetofbasicallyinactivefiledescriptorsthat *stayunchangedpresentasweprocessmanyevents. * *However,wehavetobecarefulthatwedon'thitthefollowingracecondition *SelectThreadMainThread *-------------------------------- *PollingCompletes *Readsdataorotherwisechangesfiledescriptorstate *Checksifpollingiscurrent *Doesnothing(*) *Releaseslock *Acquireslock *Markspollingascomplete *Wakesmainthread *Receivesoldstalefiledescriptorstate * *Toavoidthis,whenthenewsetofpolldescriptorsisthesameasthecurrent *one,wetransitiontothePOLLING_RESTARTstageatthepointmarked(*).When *theselectthreadwakesupfromthepollbecauseafiledescriptorisactive,if *thestateisPOLLING_RESTARTitimmediatelybeginspollingsamethefiledescriptor *setagain.Thisnormallywilljustreturnthesamesetofactivefiledescriptors *asthefirsttime,butinsequencedescribedabovewillproperlyupdatethe *filedescriptorstate. * *Specialcase:thisRESTARTlogicisnotneedediftheonlyFDistheinternalGLib *"wakeuppipe"thatispresentedwhenthreadsareinitialized. * *P.S.:Theharmintheabovesequenceismostlythatsourcescanbesignalled *asreadywhentheyarenolongerready.Thismaypromptablockingread *fromafiledescriptorthathangs.
*/ if (!pollfds_equal (ufds, nfds, current_pollfds, current_n_pollfds - 1))
have_new_pollfds = TRUE; else
{ if (!((nfds == 1 && poll_fd_index < 0) ||
(nfds == 2 && poll_fd_index >= 0)))
select_thread_set_state (POLLING_RESTART);
}
} else
have_new_pollfds = TRUE;
if (have_new_pollfds)
{
GDK_DEBUG (EVENTLOOP, "EventLoop: Submitting a new set of file descriptor to the select thread");
if (select_thread_state != POLLING_QUEUED && select_thread_state != WAITING)
{ if (select_thread_wakeup_pipe[1])
{ char c = 'A';
write (select_thread_wakeup_pipe[1], &c, 1);
}
}
select_thread_set_state (POLLING_QUEUED);
}
SELECT_THREAD_UNLOCK ();
return -1;
}
/* End an asynchronous polling operation started with *select_thread_collect_poll().Thismustbecalledifandonlyif *select_thread_start_poll()return-1.TheGPollFDarraypassed *inmustbeidenticaltotheonepassedtoselect_thread_start_poll(). * *TheresultsofthepollarewrittenintotheGPollFDarraypassedin. * *Returns:numberoffiledescriptorsready
*/ staticint
select_thread_collect_poll (GPollFD *ufds, guint nfds)
{ int i; int n_ready = 0;
SELECT_THREAD_LOCK ();
if (select_thread_state == WAITING) /* The poll completed */
{ for (i = 0; i < nfds; i++)
{ if (ufds[i].fd == -1) continue;
if (current_events)
event = g_queue_pop_tail (current_events);
return event;
}
staticvoid
_gdk_macos_event_source_queue_event (NSEvent *event)
{ /* Just used to wake us up; if an event and a FD arrived at the same *time;couldhavecomefromapreviousiterationinsomecases, *butthespuriouswakeupisharmlessifalittleinefficient.
*/ if (!event ||
([event type] == NSEventTypeApplicationDefined &&
[event subtype] == GDK_MACOS_EVENT_SUBTYPE_EVENTLOOP)) return;
/* Here last_ufds might have changed since the time this function was *called.Itispossiblethatarecursivemainloop(andthusrecursive *invocationofthispollfunction)istriggeredwhilein *nextEventMatchingMask:.Ifduringthattimenewfdsareadded, *thecachedfdsarraymightbereplaceding_main_context_iterate(), *orifthefdschange,theymightbereplacedinthearraycontentsand *notmatchtheonesthecollectstageexpects.Sowecheckifweentered *anynestedmainloop(checkingtheufdspointerchangedisnotenough). *Weavoidthisbynotcallingthecollectstage.
*/ if (execution_id == execution_count && n_ready < 0)
n_ready = select_thread_collect_poll (ufds, nfds);
/* Wrapper around g_main_context_query() that handles reallocating *run_loop_pollfdsuptothepropersize
*/ staticint
query_main_context (GMainContext *context, int max_priority, int *timeout)
{ int nfds;
staticvoid
run_loop_entry (void)
{ if (acquired_loop_level == -1)
{ if (g_main_context_acquire (NULL))
{
GDK_DEBUG (EVENTLOOP, "EventLoop: Beginning tracking run loop activity");
acquired_loop_level = current_loop_level;
} else
{ /* If we fail to acquire the main context, that means someone is iterating *themaincontextinadifferentthread;wesimplywaituntilthisloop *exitsandthentryagainatnextentry.Ingeneral,iteratingtheloop *fromadifferentthreadisrare:itisonlypossiblewhenGDKthreading *isinitializedandisnotfrequentlyusedeventhen.So,wehopethat *havingGLibmainloopiterationblockedinthecombinationofthatand *anativemodaloperationisaminimalproblem.Wecouldimagineusinga *threadthatdoesg_main_context_wait()andthenwakesusbackup,but *thegaindoesn'tseemworththecomplexity.
*/
GDK_DEBUG (EVENTLOOP, "EventLoop: Can't acquire main loop; skipping tracking run loop activity");
}
}
}
staticvoid
run_loop_before_timers (void)
{
}
staticvoid
run_loop_before_sources (void)
{
GMainContext *context = g_main_context_default (); int max_priority; int nfds;
/* Before we let the CFRunLoop process sources, we want to check if there *areanypendingGLibmainloopsourcesmoreurgentthan *G_PRIORITY_DEFAULTthatneedtobedispatched.(Weconsiderallactivity *fromtheCFRunLooptohaveapriorityofG_PRIORITY_DEFAULT.)Ifno *sourcesareprocessedbytheCFRunLoop,thenprocessingwillcontinue *ontotheBeforeWaitingstagewherewecheckforlowerprioritysources.
*/
g_main_context_prepare (context, &max_priority);
max_priority = MIN (max_priority, G_PRIORITY_DEFAULT);
/* We ignore the timeout that query_main_context () returns since we'll *alwaysqueryagainbeforewaiting.
*/
nfds = query_main_context (context, max_priority, NULL);
if (nfds)
old_poll_func (run_loop_pollfds, nfds, 0);
if (g_main_context_check (context, max_priority, run_loop_pollfds, nfds))
{
GDK_DEBUG (EVENTLOOP, "EventLoop: Dispatching high priority sources");
g_main_context_dispatch (context);
}
}
staticvoid
dummy_timer_callback (CFRunLoopTimerRef timer, void *info)
{ /* Nothing; won't normally even be called */
}
staticvoid
run_loop_before_waiting (void)
{
GMainContext *context = g_main_context_default (); int timeout; int n_ready;
/* At this point, the CFRunLoop is ready to wait. We start a GMain loop *iterationbycallingthecheck()andquery()stages.Westarta *poll,andifitdoesn'tcompleteimmediatelywelettherunloop *goaheadandsleep.Beforedoingthat,iftherewasatimeoutfrom *GLib,wesetupaCFRunLoopTimertowakeusup.
*/
if (n_ready > 0 || timeout == 0)
{ /* We have stuff to do, no sleeping allowed! */
CFRunLoopWakeUp (main_thread_run_loop);
} elseif (timeout > 0)
{ /* We need to get the run loop to break out of its wait when our timeout *expires.Wedothisbyaddingadummytimerthatwe'llremoveimmediately *afterthewaitwakesup.
*/
GDK_DEBUG (EVENTLOOP, "EventLoop: Adding timer to wake us up in %d milliseconds", timeout);
if (run_loop_polling_async)
{
select_thread_collect_poll (run_loop_pollfds, run_loop_n_pollfds);
run_loop_polling_async = FALSE;
}
if (g_main_context_check (context, run_loop_max_priority, run_loop_pollfds, run_loop_n_pollfds))
{
GDK_DEBUG (EVENTLOOP, "EventLoop: Dispatching after waiting");
g_main_context_dispatch (context);
}
}
staticvoid
run_loop_exit (void)
{ /* + 1 because we decrement current_loop_level separately in observer_callback() */ if ((current_loop_level + 1) == acquired_loop_level)
{
g_main_context_release (NULL);
acquired_loop_level = -1;
GDK_DEBUG (EVENTLOOP, "EventLoop: Ended tracking run loop activity");
}
}
staticvoid
run_loop_observer_callback (CFRunLoopObserverRef observer,
CFRunLoopActivity activity, void *info)
{ switch (activity)
{ case kCFRunLoopEntry:
current_loop_level++; break; case kCFRunLoopExit:
g_return_if_fail (current_loop_level > 0);
current_loop_level--; break; case kCFRunLoopBeforeTimers: case kCFRunLoopBeforeSources: case kCFRunLoopBeforeWaiting: case kCFRunLoopAfterWaiting: case kCFRunLoopAllActivities: default: break;
}
/* DnD starts a nested runloop, or so it seems. Ifwehavesuchaloop,westillwanttorun
our idle handlers. */ if (getting_events > 0 && current_loop_level < 2) return;
switch (activity)
{ case kCFRunLoopEntry:
run_loop_entry (); break; case kCFRunLoopBeforeTimers:
run_loop_before_timers (); break; case kCFRunLoopBeforeSources:
run_loop_before_sources (); break; case kCFRunLoopBeforeWaiting:
run_loop_before_waiting (); break; case kCFRunLoopAfterWaiting:
run_loop_after_waiting (); break; case kCFRunLoopExit:
run_loop_exit (); break; case kCFRunLoopAllActivities: default: break;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.