@@ -252,7 +252,7 @@ def Event(self):
252
252
def get_execmodel (backend ):
253
253
if hasattr (backend , "backend" ):
254
254
return backend
255
- if backend == "thread" :
255
+ if backend in ( "thread" , "main_thread_only" ) :
256
256
return ThreadExecModel ()
257
257
elif backend == "eventlet" :
258
258
return EventletExecModel ()
@@ -322,7 +322,7 @@ def __init__(self, execmodel, hasprimary=False):
322
322
self ._shuttingdown = False
323
323
self ._waitall_events = []
324
324
if hasprimary :
325
- if self .execmodel .backend != "thread" :
325
+ if self .execmodel .backend not in ( "thread" , "main_thread_only" ) :
326
326
raise ValueError ("hasprimary=True requires thread model" )
327
327
self ._primary_thread_task_ready = self .execmodel .Event ()
328
328
else :
@@ -332,7 +332,7 @@ def integrate_as_primary_thread(self):
332
332
"""integrate the thread with which we are called as a primary
333
333
thread for executing functions triggered with spawn().
334
334
"""
335
- assert self .execmodel .backend == "thread" , self .execmodel
335
+ assert self .execmodel .backend in ( "thread" , "main_thread_only" ) , self .execmodel
336
336
primary_thread_task_ready = self ._primary_thread_task_ready
337
337
# interacts with code at REF1
338
338
while 1 :
@@ -345,7 +345,11 @@ def integrate_as_primary_thread(self):
345
345
with self ._running_lock :
346
346
if self ._shuttingdown :
347
347
break
348
- primary_thread_task_ready .clear ()
348
+ # Only clear if _try_send_to_primary_thread has not
349
+ # yet set the next self._primary_thread_task reply
350
+ # after waiting for this one to complete.
351
+ if reply is self ._primary_thread_task :
352
+ primary_thread_task_ready .clear ()
349
353
350
354
def trigger_shutdown (self ):
351
355
with self ._running_lock :
@@ -376,6 +380,19 @@ def _try_send_to_primary_thread(self, reply):
376
380
# wake up primary thread
377
381
primary_thread_task_ready .set ()
378
382
return True
383
+ elif (
384
+ self .execmodel .backend == "main_thread_only"
385
+ and self ._primary_thread_task is not None
386
+ ):
387
+ self ._primary_thread_task .waitfinish ()
388
+ self ._primary_thread_task = reply
389
+ # wake up primary thread (it's okay if this is already set
390
+ # because we waited for the previous task to finish above
391
+ # and integrate_as_primary_thread will not clear it when
392
+ # it enters self._running_lock if it detects that a new
393
+ # task is available)
394
+ primary_thread_task_ready .set ()
395
+ return True
379
396
return False
380
397
381
398
def spawn (self , func , * args , ** kwargs ):
@@ -1106,7 +1123,18 @@ def join(self, timeout=None):
1106
1123
class WorkerGateway (BaseGateway ):
1107
1124
def _local_schedulexec (self , channel , sourcetask ):
1108
1125
sourcetask = loads_internal (sourcetask )
1109
- self ._execpool .spawn (self .executetask , (channel , sourcetask ))
1126
+ if self .execmodel .backend == "main_thread_only" :
1127
+ # TODO: Maybe use something like queue.Queue to queue an asynchronous
1128
+ # spawn here in order to avoid using another thread.
1129
+ import threading
1130
+
1131
+ t = threading .Thread (
1132
+ target = self ._execpool .spawn ,
1133
+ args = (self .executetask , (channel , sourcetask )),
1134
+ )
1135
+ t .start ()
1136
+ else :
1137
+ self ._execpool .spawn (self .executetask , (channel , sourcetask ))
1110
1138
1111
1139
def _terminate_execution (self ):
1112
1140
# called from receiverthread
@@ -1132,7 +1160,7 @@ def serve(self):
1132
1160
def trace (msg ):
1133
1161
self ._trace ("[serve] " + msg )
1134
1162
1135
- hasprimary = self .execmodel .backend == "thread"
1163
+ hasprimary = self .execmodel .backend in ( "thread" , "main_thread_only" )
1136
1164
self ._execpool = WorkerPool (self .execmodel , hasprimary = hasprimary )
1137
1165
trace ("spawning receiver thread" )
1138
1166
self ._initreceive ()
0 commit comments