#lang scribble/doc
@(require "mz.rkt"
          (for-label racket/engine))

@title[#:tag "engine"]{Engines}

@note-lib-only[racket/engine]

An @deftech{engine} is an abstraction that models processes that
can be preempted by a timer or other external trigger. They are
inspired by the work of Haynes and Friedman @cite["Haynes84"].

Engines log their behavior via a logger with the name 
@racket['racket/engine]. The logger is created when the module
is instantiated and uses the result of @racket[(current-logger)]
as its parent. The library adds logs a @racket['debug] level
message: when @racket[engine-run]
is called, when the engine timeout expires, and when the engine is
stopped (either because it terminated or it reached a safe point to
stop). Each log message holds a value of the struct:
@racketblock[(struct engine-info (msec name) #:prefab)]
where the @racket[_msec] field holds the result of 
@racket[(current-inexact-milliseconds)] at the moment of logging,
and the @racket[_name] field holds the name of the procedure
passed to @racket[engine].

@defproc[(engine [proc ((any/c . -> . void?) . -> . any/c)])
         engine?]{

Returns an engine object to encapsulate a thread that runs only when
allowed. The @racket[proc] procedure should accept one argument, and
@racket[proc] is run in the engine thread when
@racket[engine-run] is called. If @racket[engine-run] returns
due to a timeout, then the engine thread is suspended until a
future call to @racket[engine-run]. Thus, @racket[proc] only
executes during the dynamic extent of a @racket[engine-run] call.

The argument to @racket[proc] is a procedure that takes a boolean, and
it can be used to disable suspends (in case @racket[proc] has critical
regions where it should not be suspended). A true value passed to the
procedure enables suspends, and @racket[#f] disables
suspends. Initially, suspends are allowed.}


@defproc[(engine? [v any/c]) any]{

Returns @racket[#t] if @racket[v] is an engine produced by
@racket[engine], @racket[#f] otherwise.}


@defproc[(engine-run [until (or/c evt? real?)][engine engine?])
         boolean?]{

Allows the thread associated with @racket[engine] to execute for up
as long as @racket[until] milliseconds (of @racket[until] is a real
number) or @racket[until] is ready (if @racket[until] is an event). If
@racket[engine]'s procedure disables suspends, then the engine
can run arbitrarily long until it re-enables suspends.

The @racket[engine-run] procedure returns @racket[#t] if
@racket[engine]'s procedure completes (or if it completed earlier),
and the result is available via @racket[engine-result].  The
@racket[engine-run] procedure returns @racket[#f] if
@racket[engine]'s procedure does not complete before it is
suspended after @racket[timeout-secs]. If @racket[engine]'s
procedure raises an exception, then it is re-raised by
@racket[engine-run].}


@defproc[(engine-result [engine engine]) any]{

Returns the result for @racket[engine] if it has completed with a
value (as opposed to an exception), @racket[#f] otherwise.}


@defproc[(engine-kill [engine engine?]) void?]{

Forcibly terminates the thread associated with @racket[engine] if
it is still running, leaving the engine result unchanged.}
