Fan

 

const class

sys::Thread

sys::Obj
  sys::Thread
//
// Copyright (c) 2006, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   26 Dec 06  Brian Frank  Stub
//   28 Jan 07  Brian Frank  Implement threading model
//

**
** Thread models a thread of execution within a process.
** See `docLang::Threading` for details.
**
const class Thread
{

//////////////////////////////////////////////////////////////////////////
// Construction
//////////////////////////////////////////////////////////////////////////

  **
  ** Make a new thread with the given name.  If name is non-null then it
  ** must not conflict with any threads currently active (new or running)
  ** otherwise ArgErr is thrown.  Convention is to use a dotted notation
  ** beginning with your pod name to avoid naming collisions.  If name is
  ** null, then a unique name is automatically generated.  If name is
  ** non-null, then it must be valid according to `Uri.isName` otherwise
  ** NameErr is thrown.
  **
  ** If run is non-null, then it is invoked as the main loop of the thread.
  ** If run is specified then it must be an immutable function (it cannot
  ** capture state from the calling thread), otherwise NotImmutableErr is
  ** thrown.  If run is null, then you must subclass Thread and override
  ** the run() method.  The return value of run is available to the first
  ** thread which calls `join`.
  **
  ** The thread is created in the new state, and must be started using
  ** the start method.
  **
  new make(Str? name := null, |Thread t->Obj|? run := null)

//////////////////////////////////////////////////////////////////////////
// Management
//////////////////////////////////////////////////////////////////////////

  **
  ** Lookup a thread in this VM by name.  If the thread doesn't
  ** exist and checked is false then return null, otherwise throw
  ** UnknownThreadErr.  Only active threads which are in the new
  ** or running state may be found by name.
  **
  static Thread? find(Str name, Bool checked := true)

  **
  ** Get the list of all active (new or running) threads
  ** in the VM.
  **
  static Thread[] list()

  **
  ** Get the currently executing thread.  Throw Err if
  ** the current thread is not a proper Fan thread.
  **
  static Thread current()

  **
  ** Return the map of thread local variables.  This is a map of "global"
  ** variables visible only to the current thread.  These variables are
  ** keyed by a string name - by convention use a dotted notation beginning
  ** with your pod name to avoid naming collisions.
  **
  static Str:Obj? locals()

//////////////////////////////////////////////////////////////////////////
// Service
//////////////////////////////////////////////////////////////////////////

  **
  ** Lookup a service thread by type.  If the service doesn't
  ** exist and checked is false then return null, otherwise throw
  ** UnknownThreadErr.  See `isService`.
  **
  static Thread? findService(Type t, Bool checked := true)

  **
  ** Subclasses should override this method to publish this
  ** thread to a "well known URI".  Service threads are automatically
  ** mapped into the namespace under "/sys/service/{qname}" for
  ** all their public types.  This mapping is only available while
  ** the thread is new or running.  If more than service type is currently
  ** active, only the first one is mapped.  You can also use the
  ** `findService` method to lookup a service type.  The default is
  ** to return false.
  **
  virtual Bool isService()

//////////////////////////////////////////////////////////////////////////
// Identity
//////////////////////////////////////////////////////////////////////////

  **
  ** Get the name of this thread which uniquely
  ** identifies this thread within the VM.
  **
  Str name()

  **
  ** Print this thread's stack trace to the specified output
  ** stream (or Sys.out by default).  If this thread is not currently
  ** running print nothing.
  **
  Void trace(OutStream out := Sys.out)

  **
  ** Return true if same thread according '===' same operator.
  **
  override Bool equals(Obj? obj)

  **
  ** Return name.hash.
  **
  override Int hash()

  **
  ** Default toStr returns name.
  **
  override Str toStr()

//////////////////////////////////////////////////////////////////////////
// State
//////////////////////////////////////////////////////////////////////////

  **
  ** Return if this thread has been created, but not yet started.
  **
  Bool isNew()

  **
  ** Return if this thread has been started, but not yet stopped.
  **
  Bool isRunning()

  **
  ** Return if this thread has been stopped.
  **
  Bool isDead()

//////////////////////////////////////////////////////////////////////////
// Lifecycle
//////////////////////////////////////////////////////////////////////////

  **
  ** Start this thread running.  If the thread is already
  ** running or has been stopped, then throw Err.  Return this.
  **
  This start()

  **
  ** Stop this thread from running.  If this thread
  ** is not currently running, then this method does nothing.
  ** Note that the thread likely doesn't actually terminate
  ** until it reaches an interruptable point in its main loop.
  ** Return this.
  **
  This stop()

  **
  ** Wait for this thread to stop.  If timeout is non-null,
  ** then wait no longer then specified timeout.  If this thread
  ** hasn't been started yet, then throw Err.  If this thread
  ** is already dead, then this method is a no op.  Return
  ** the result of the run method for the first thread to join,
  ** or null on subsequent calls.
  **
  Obj? join(Duration? timeout := null)

  **
  ** Put the currently executing thread to sleep for the
  ** specified period.  If the thread is interrupted for any
  ** reason while sleeping, then InterruptedErr is thrown.
  **
  static Void sleep(Duration duration)

  **
  ** The run method implements the code to run in the thread.
  ** If a run function was specified in the constructor, then it
  ** is invoked, otherwise subclasses should override this method.
  ** Threads which wish to process their message queue must
  ** enter the main loop by calling the loop() method.  The
  ** return value of this method is available to the first thread
  ** which calls the join method (the result is not required to
  ** be immutable).
  **
  protected virtual Obj? run()

  **
  ** This callback is invoked on this thread right before
  ** `run` is called.  If this method raises an exception,
  ** then `run` is not called (although `onStop` is still
  ** called).
  **
  protected virtual Void onStart()

  **
  ** This callback is invoked on this thread right after
  ** the `run` method exits.  This method is called even
  ** if `onStart` or `run` raises an exception.
  **
  protected virtual Void onStop()

  **
  ** Enter the message loop.  This method does not return until
  ** the thread is stopped.  This receive callback is invoked by
  ** the main loop each time a message is received from its send queue.
  ** The callback should process the message and return a response.
  ** If the calling thread is not this thread, then throw Err.
  **
  ** If the the message was enqueued by sendAsync the response is
  ** ignored; exceptions are printed to standard output and ignored.
  **
  ** If the message was enqueued by sendSync the response is
  ** returned to the caller and must be immutable or serializable;
  ** exceptions are raised to the caller.
  **
  ** See [docLang]`docLang::Threading#messages`.
  **
  Void loop(|Obj msg->Obj| receive)

//////////////////////////////////////////////////////////////////////////
// Messaging
//////////////////////////////////////////////////////////////////////////

  **
  ** Enqueue the specified message for this thread, then block the
  ** calling thread until this thread processes the message via
  ** the `loop` callback - return the result.  If the `loop`
  ** callback throws an exception processing the message, then
  ** that exception is raised to the calling thread.  If msg is
  ** not immutable or serializable, then IOErr is thrown.  If this
  ** thread is stopped while the caller is blocked, then an
  ** InterruptedErr is thrown.  Note that flow control may block
  ** the caller until there is enough space in this thread's message
  ** queue.
  **
  ** See [docLang]`docLang::Threading#messages`.
  **
  Obj? sendSync(Obj? msg)

  **
  ** Enqueue the specified message for this thread to process in
  ** its received() callback.  Using sendAsync() is fire-and-forget,
  ** the caller has no guarantee that this thread will successfully
  ** process the message.  If msg is not immutable or serializable,
  ** then IOErr is thrown.  Note that flow control may block the
  ** caller until there is enough space in this thread's message
  ** queue.  Return this.
  **
  ** See [docLang]`docLang::Threading#messages`.
  **
  This sendAsync(Obj? msg)

  **
  ** Setup a timer to post a message to this thread after the
  ** specified duration has elapsed.  If repeat is true, then
  ** the same msg is posted on repeated intervals of the given
  ** duration.  Expired timer messages are always processed before
  ** messages posted by `sendSync` and `sendAsync`.  Return an opaque
  ** ticket which may used to cancel the timer via `cancelLater`.
  ** If msg is not immutable or serializable, then IOErr is thrown.
  **
  ** See [docLang]`docLang::Threading#timers`.
  **
  Obj sendLater(Duration dur, Obj? msg, Bool repeat := false)

  **
  ** Cancel a timer with the ticket returned by `sendLater`.
  **
  Void cancelLater(Obj ticket)

}