Module: target-application-internals Synopsis: Defining a class that has special functionality for multi-threaded access. Author: Paul Howard, Andy Armstrong Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND define function thread-debug-message (string :: , #rest pants) => () if (*debugging-debugger?*) let control = concatenate(current-thread().thread-name | "???", " : ", string); apply(debugger-message, control, pants) end if end function; define constant = type-union(, , singleton(#f)); ///// // A that contains synchronization functionality. This // class is used by the debugger and by the application-server backend // for environment objects. define class () // Lock and serialize all calls into the DM. // Use a single-exclusive/multiple-inclusive lock over the entire // debugger session, to lock all threads out until the Debugger // Manager thread itself effortlessly releases its exclusive rights // to the session lock. constant slot debugger-session :: = make(); // A lock on the current debugger transaction to synchronize threads // requesting spy calls on DM or requesting DM to continue the application constant slot debugger-transaction :: = make(); slot debugger-transaction-timeout = #f; // A lock for client threads when requesting debugger transactions to // lock out other requesting threads until DM has served our request constant slot debugger-transaction-request :: = make(); // A lock for enabling client threads to optionally synchronize // with the Debugger Manager on application termination constant slot application-shut-down-lock :: = make(); // Record the current stop reason for the application slot current-stop-reason :: ; // Store the thread that runs the DM loop, as well as any thread // that gains exclusive debug access. slot manager-thread :: ; slot thread-being-served :: false-or() = #f; // Two notifications that can be used anywhere in the UI. These // get notified when debugger transactions start and finish. slot debugger-transaction-notification :: ; slot debugger-transaction-complete :: ; // A boolean flag to indicate whether a debugger transaction is // currently in effect. slot in-debugger-transaction? :: , init-value: #f; // Internal Slots. slot under-management? :: , init-value: #f; slot been-managed? :: , init-value: #f; slot stored-interactor-handler :: ; // Support for "interrupting" transactions (to perform spy calls // without officially ending the transaction) slot interruption-evaluated :: ; slot interruption-function :: = #f; slot interruption-results :: = #[]; slot stored-library-initialization-phase-handler :: ; end class; ///// NULL-STOP-REASON-CALLBACK // Should never get called define method null-stop-reason-callback (application :: , sr :: ) => (bool :: ) #f end method; ///// NULL-INTERACTOR-CALLBACK // We don't care about the return values of interactive evaluations? define method null-interactor-callback (application :: , thread :: , transaction-id :: , #rest return-values) => (answer :: ) #f end method; define method null-library-initialization-phase-callback (application :: , thread :: , remote-library :: , phase :: , top-level? :: ) => (interested? :: ) #f end method; define method null-application-state-callback (application :: , new-state :: ) => () end method; /// OBTAIN-COMPONENT-NAME /// /// Get the DLL name for a given library /// XXX does not work with library packs! find-library-info is broken /// we hope nobody calls it (it wasn't called during our debugging sessions) define method obtain-component-name (application :: , library-name :: ) => (component-name :: ) let info = find-library-info(as(, library-name)); case info => info.info-binary-name; otherwise => library-name; end end method obtain-component-name; ///// HANDLE-LIBRARY-INITIALIZATION-PHASE // The method for an environment . define method handle-library-initialization-phase (application :: , thread :: , remote-library :: , phase :: , top-level? :: ) => (interested? :: ) let interested? = application.stored-library-initialization-phase-handler (application, thread, remote-library, phase, top-level?); interested? end method; ///// HANDLE-INTERACTOR-RETURN // The method for an environment . define method handle-interactor-return (application :: , thread :: , transaction-id :: , #rest return-values) => (answer :: ) next-method(); let answer = apply(application.stored-interactor-handler, application, thread, transaction-id, return-values); answer end method; ///// // A stop reason that is not propagated to the environment. define class () end class; ///// RUN-TARGET-APPLICATION // This is basically like MANAGE-RUNNING-APPLICATION, except that it // does not require callback parameters. This library exports open // generic functions that are used as the callbacks. // This function runs on the calling thread, and does not return until // the application quits. define method run-target-application (application :: , #key stop-reason-callback = null-stop-reason-callback, dt-prolog = #f, dt-epilog = #f, interactor-callback = null-interactor-callback, library-init-callback = null-library-initialization-phase-callback, application-state-callback = null-application-state-callback) => () local method manage-stop-reason (app :: , sr :: ) => (interested? :: ) thread-debug-message ("Entering stop-reason callback for %=", sr); let stopping? :: = if (instance?(sr, )) #t else block() stop-reason-callback(app, sr) exception() #f end block; end if; thread-debug-message("Returning %= from sr callback", stopping?); stopping? end method; local method manage-debugger-transaction-for-stop-reason (app :: , sr :: ) => () unless (instance?(sr, )) application-state-callback(app, #"stopped"); if (dt-prolog) thread-debug-message("Running debugger transaction prolog"); block() dt-prolog(app, sr) exception() values() end block end if; end unless; app.current-stop-reason := sr; manage-debugger-transaction(app); unless (instance?(sr, )) if (dt-epilog) thread-debug-message("Running debugger transaction epilog"); block() dt-epilog(app, sr) exception() values() end block end if; application-state-callback(app, #"running"); end unless; end method; if (application.been-managed?) error("This application has run and terminated already"); elseif (application.under-management?) error("This application is already running"); else with-lock (application.application-shut-down-lock) application.under-management? := #t; application.manager-thread := current-thread(); application.stored-interactor-handler := interactor-callback; application.stored-library-initialization-phase-handler := library-init-callback; application-state-callback(application, #"uninitialized"); with-lock (application.debugger-session, mode: #"write") manage-running-application (application, stop-reason-callback: manage-stop-reason, ready-to-continue-callback: manage-debugger-transaction-for-stop-reason); application-state-callback(application, #"closed"); application.under-management? := #f; application.been-managed? := #t; end with-lock; end with-lock; end if; end method; ///// INITIALIZE () // Sets up the notification fields, and associates them with the // debugger transaction lock. define method initialize (application :: , #rest keys, #key, #all-keys) => () next-method(); application.debugger-transaction-notification := make(, lock: application.debugger-transaction); application.debugger-transaction-complete := make(, lock: application.debugger-transaction); application.interruption-evaluated := make(, lock: application.debugger-transaction); end method;