@@ -134,6 +134,10 @@ def Event(self):
134134 return threading .Event ()
135135
136136
137+ class MainThreadOnlyExecModel (ThreadExecModel ):
138+ backend = "main_thread_only"
139+
140+
137141class EventletExecModel (ExecModel ):
138142 backend = "eventlet"
139143
@@ -254,6 +258,8 @@ def get_execmodel(backend):
254258 return backend
255259 if backend == "thread" :
256260 return ThreadExecModel ()
261+ elif backend == "main_thread_only" :
262+ return MainThreadOnlyExecModel ()
257263 elif backend == "eventlet" :
258264 return EventletExecModel ()
259265 elif backend == "gevent" :
@@ -322,7 +328,7 @@ def __init__(self, execmodel, hasprimary=False):
322328 self ._shuttingdown = False
323329 self ._waitall_events = []
324330 if hasprimary :
325- if self .execmodel .backend != "thread" :
331+ if self .execmodel .backend not in ( "thread" , "main_thread_only" ) :
326332 raise ValueError ("hasprimary=True requires thread model" )
327333 self ._primary_thread_task_ready = self .execmodel .Event ()
328334 else :
@@ -332,7 +338,7 @@ def integrate_as_primary_thread(self):
332338 """integrate the thread with which we are called as a primary
333339 thread for executing functions triggered with spawn().
334340 """
335- assert self .execmodel .backend == "thread" , self .execmodel
341+ assert self .execmodel .backend in ( "thread" , "main_thread_only" ) , self .execmodel
336342 primary_thread_task_ready = self ._primary_thread_task_ready
337343 # interacts with code at REF1
338344 while 1 :
@@ -345,7 +351,11 @@ def integrate_as_primary_thread(self):
345351 with self ._running_lock :
346352 if self ._shuttingdown :
347353 break
348- primary_thread_task_ready .clear ()
354+ # Only clear if _try_send_to_primary_thread has not
355+ # yet set the next self._primary_thread_task reply
356+ # after waiting for this one to complete.
357+ if reply is self ._primary_thread_task :
358+ primary_thread_task_ready .clear ()
349359
350360 def trigger_shutdown (self ):
351361 with self ._running_lock :
@@ -376,6 +386,19 @@ def _try_send_to_primary_thread(self, reply):
376386 # wake up primary thread
377387 primary_thread_task_ready .set ()
378388 return True
389+ elif (
390+ self .execmodel .backend == "main_thread_only"
391+ and self ._primary_thread_task is not None
392+ ):
393+ self ._primary_thread_task .waitfinish ()
394+ self ._primary_thread_task = reply
395+ # wake up primary thread (it's okay if this is already set
396+ # because we waited for the previous task to finish above
397+ # and integrate_as_primary_thread will not clear it when
398+ # it enters self._running_lock if it detects that a new
399+ # task is available)
400+ primary_thread_task_ready .set ()
401+ return True
379402 return False
380403
381404 def spawn (self , func , * args , ** kwargs ):
@@ -1132,7 +1155,7 @@ def serve(self):
11321155 def trace (msg ):
11331156 self ._trace ("[serve] " + msg )
11341157
1135- hasprimary = self .execmodel .backend == "thread"
1158+ hasprimary = self .execmodel .backend in ( "thread" , "main_thread_only" )
11361159 self ._execpool = WorkerPool (self .execmodel , hasprimary = hasprimary )
11371160 trace ("spawning receiver thread" )
11381161 self ._initreceive ()
0 commit comments