@@ -475,23 +475,19 @@ def thunk():
475475# IDA Async Magic
476476#------------------------------------------------------------------------------
477477
478- @mainthread
479- def await_future (future , block = True , timeout = 1.0 ):
478+ def await_future (future ):
480479 """
481480 This is effectively a technique I use to get around completely blocking
482481 IDA's mainthread while waiting for a threaded result that may need to make
483- use of the sync operators.
482+ use of the execute_sync operators.
484483
485484 Waiting for a 'future' thread result to come through via this function
486485 lets other execute_sync actions to slip through (at least Read, Fast).
487486 """
488-
489- elapsed = 0 # total time elapsed processing this future object
490487 interval = 0.02 # the interval which we wait for a response
491- end_time = time .time () + timeout
492488
493- # run until the the future completes or the timeout elapses
494- while block or ( time . time () < end_time ) :
489+ # run until the the future arrives
490+ while True :
495491
496492 # block for a brief period to see if the future completes
497493 try :
@@ -503,26 +499,75 @@ def await_future(future, block=True, timeout=1.0):
503499 #
504500
505501 except Queue .Empty as e :
506- logger .debug ("Flushing future..." )
502+ pass
503+
504+ logger .debug ("Awaiting future..." )
505+
506+ #
507+ # if we are executing (well, blocking) as the main thread, we need
508+ # to flush the event loop so IDA does not hang
509+ #
510+
511+ if idaapi .is_main_thread ():
512+ flush_ida_sync_requests ()
513+
514+ def await_lock (lock ):
515+ """
516+ Attempt to acquire a lock without blocking the IDA mainthread.
517+
518+ See await_future() for more details.
519+ """
520+
521+ elapsed = 0 # total time elapsed waiting for the lock
522+ interval = 0.02 # the interval (in seconds) between acquire attempts
523+ timeout = 60.0 # the total time allotted to acquiring the lock
524+ end_time = time .time () + timeout
525+
526+ # wait until the the lock is available
527+ while time .time () < end_time :
528+
529+ #
530+ # attempt to acquire the given lock without blocking (via 'False').
531+ # if we succesfully aquire the lock, then we can return (success)
532+ #
533+
534+ if lock .acquire (False ):
535+ logger .debug ("Acquired lock!" )
536+ return
537+
538+ #
539+ # the lock is not available yet. we need to sleep so we don't choke
540+ # the cpu, and try to acquire the lock again next time through...
541+ #
542+
543+ logger .debug ("Awaiting lock..." )
544+ time .sleep (interval )
545+
546+ #
547+ # if we are executing (well, blocking) as the main thread, we need
548+ # to flush the event loop so IDA does not hang
549+ #
550+
551+ if idaapi .is_main_thread ():
507552 flush_ida_sync_requests ()
508553
554+ #
555+ # we spent 60 seconds trying to acquire the lock, but never got it...
556+ # to avoid hanging IDA indefinitely (or worse), we abort via signal
557+ #
558+
559+ raise RuntimeError ("Failed to acquire lock after %f seconds!" % timeout )
560+
509561@mainthread
510562def flush_ida_sync_requests ():
511563 """
512564 Flush all execute_sync requests.
513-
514- NOTE: This MUST be called from the IDA Mainthread to be effective.
515565 """
516- if not idaapi .is_main_thread ():
517- return False
518566
519567 # this will trigger/flush the IDA UI loop
520568 qta = QtCore .QCoreApplication .instance ()
521569 qta .processEvents ()
522570
523- # done
524- return True
525-
526571@mainthread
527572def prompt_string (label , title , default = "" ):
528573 """
0 commit comments