@@ -475,23 +475,19 @@ def thunk():
475
475
# IDA Async Magic
476
476
#------------------------------------------------------------------------------
477
477
478
- @mainthread
479
- def await_future (future , block = True , timeout = 1.0 ):
478
+ def await_future (future ):
480
479
"""
481
480
This is effectively a technique I use to get around completely blocking
482
481
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.
484
483
485
484
Waiting for a 'future' thread result to come through via this function
486
485
lets other execute_sync actions to slip through (at least Read, Fast).
487
486
"""
488
-
489
- elapsed = 0 # total time elapsed processing this future object
490
487
interval = 0.02 # the interval which we wait for a response
491
- end_time = time .time () + timeout
492
488
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 :
495
491
496
492
# block for a brief period to see if the future completes
497
493
try :
@@ -503,26 +499,75 @@ def await_future(future, block=True, timeout=1.0):
503
499
#
504
500
505
501
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 ():
507
552
flush_ida_sync_requests ()
508
553
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
+
509
561
@mainthread
510
562
def flush_ida_sync_requests ():
511
563
"""
512
564
Flush all execute_sync requests.
513
-
514
- NOTE: This MUST be called from the IDA Mainthread to be effective.
515
565
"""
516
- if not idaapi .is_main_thread ():
517
- return False
518
566
519
567
# this will trigger/flush the IDA UI loop
520
568
qta = QtCore .QCoreApplication .instance ()
521
569
qta .processEvents ()
522
570
523
- # done
524
- return True
525
-
526
571
@mainthread
527
572
def prompt_string (label , title , default = "" ):
528
573
"""
0 commit comments