pike.git / lib / modules / Thread.pmod

version» Context lines:

pike.git/lib/modules/Thread.pmod:1:   #pike __REAL_VERSION__      #if constant(thread_create)   constant Thread=__builtin.thread_id;    - // The reason for this inherit is rather simple. - // It's now possible to write Thread Thread( ... ); - // - // This makes the interface look somewhat more thought-through. -  - // Unfortunately that is completely and utterly wrong. The loads of - // optional modifiers in this file is a telltale sign that the module - // Thread, which contains tools to use with threads, and - // Thread.Thread, which represent a specific thread, are two - // completely different things. That being the case, it's hardly wise - // to give them the same name. - // - // An example where this stunt breaks down is when this module is - // inherited: If the type name for a thread object is Thread then it's - // reasonable to believe that you can inherit Thread to make a custom - // thread class with some extra context. But not only do you get loads - // of stuff with that inherit which are meaningless to have in a - // thread instance (mutexes, queues, etc), you also get a nonworking - // thread since the create function is overridden here to avoid a - // thread being created when this module is created. - // - // Using Thread as a type name for thread objects is strongly - // discouraged. I.e. use "Thread.Thread t = Thread.Thread()" and _not_ - // "Thread t = Thread()". It's likely that this abomination is cleaned - // up eventually. - // - // /mast -  - inherit Thread; -  - // We don't want to create a thread of the module... - protected void create(mixed ... args) - { - } -  - optional Thread `()( mixed f, mixed ... args ) - { -  return thread_create( f, @args ); - } -  +    optional constant MutexKey=__builtin.mutex_key;   optional constant Mutex=__builtin.mutex;   optional constant Condition=__builtin.condition;   optional constant _Disabled=__builtin.threads_disabled;   optional constant Local=__builtin.thread_local;      optional constant thread_create = predef::thread_create;      optional constant this_thread = predef::this_thread;   
pike.git/lib/modules/Thread.pmod:265: Inside #if constant(thread_create)
   {    return f=='O' && sprintf( "%O(%d / %d)", this_program,    size(), read_tres );    }   }      //! @[Queue] implements a queue, or a pipeline. The main difference   //! between @[Queue] and @[Fifo] is that @[Queue]   //! will never block in write(), only allocate more memory.   //! + //! @fixme + //! Ought to be made API-compatible with @[ADT.Queue]. + //!   //! @seealso - //! @[Fifo] + //! @[Fifo], @[ADT.Queue]   //!   optional class Queue {    inherit Condition : r_cond;    inherit Mutex : lock;       array buffer=allocate(16);    int r_ptr, w_ptr;       //! This function returns the number of elements currently in the queue.    //!
pike.git/lib/modules/Thread.pmod:422: Inside #if constant(thread_create)
   return items;    }       protected string _sprintf( int f )    {    return f=='O' && sprintf( "%O(%d)", this_program, size() );    }   }       -  + //! A thread farm.   optional class Farm   {    protected Mutex mutex = Mutex();    protected Condition ft_cond = Condition();    protected Queue job_queue = Queue();    -  +  //! An asynchronous result.    class Result    {    int ready;    mixed value;    function done_cb;    -  +  //! @returns +  //! @int +  //! @value 1 +  //! Returns @expr{1@} when the result is available. +  //! @value 0 +  //! Returns @expr{0@} (zero) when the result hasn't +  //! arrived yet. +  //! @value -1 +  //! Returns negative on failure. +  //! @endint    int status()    {    return ready;    }    -  +  //! @returns +  //! Returns the result if available, a backtrace on failure, +  //! and @expr{0@} (zero) otherwise.    mixed result()    {    return value;    }    -  +  //! Wait for completion.    mixed `()()    {    object key = mutex->lock();    while(!ready) ft_cond->wait(key);    key = 0;    if( ready < 0 ) throw( value );    return value;    }    -  +  //! Register a callback to be called when +  //! the result is available. +  //! +  //! @param to +  //! Callback to be called. The first +  //! argument to the callback will be +  //! the result or the failure backtrace, +  //! and the second @expr{0@} (zero) on +  //! success, and @expr{1@} on failure.    void set_done_cb( function to )    {    if( ready )    to( value, ready<0 );    else    done_cb = to;    }    -  +  //! Register a failure. +  //! +  //! @param what +  //! The corresponding backtrace.    void provide_error( mixed what )    {    value = what;    ready = -1;    if( done_cb )    done_cb( what, 1 );    }    -  +  //! Register a completed result. +  //! +  //! @param what +  //! The result to register.    void provide( mixed what )    {    ready = 1;    value = what;    if( done_cb )    done_cb( what, 0 );    }          protected string _sprintf( int f )
pike.git/lib/modules/Thread.pmod:491: Inside #if constant(thread_create)
   switch( f )    {    case 't':    return "Thread.Farm().Result";    case 'O':    return sprintf( "%t(%d %O)", this, ready, value );    }    }    }    +  //! A worker thread.    protected class Handler    {    Mutex job_mutex = Mutex();    Condition cond = Condition();    array(object|array(function|array)) job;    object thread;       float total_time;    int handled, max_time;   
pike.git/lib/modules/Thread.pmod:515: Inside #if constant(thread_create)
   array(object|array(function|array)) q;    object key = job_mutex->lock();    ready = 1;    while( 1 )    {    cond->wait(key);    if( q = job )    {    mixed res, err;    int st = gethrtime(); -  if( err = catch(res = q[1][0]( @q[1][1] )) && q[0]) +  +  err = catch(res = q[1][0]( @q[1][1] )); +  +  if( q[0] ) +  { +  if( err )    ([object]q[0])->provide_error( err ); -  else if( q[0] ) +  else    ([object]q[0])->provide( res ); -  +  }    object lock = mutex->lock();    free_threads += ({ this });    lock = 0;    st = gethrtime()-st;    total_time += st/1000.0;    handled++;    job = 0;    if( st > max_time )    max_time = st;    ft_cond->broadcast();
pike.git/lib/modules/Thread.pmod:549: Inside #if constant(thread_create)
      void run( array(function|array) what, object|void resobj )    {    while(!ready) sleep(0.1);    object key = job_mutex->lock();    job = ({ resobj, what });    cond->signal();    key = 0;    }    +  //! Get some statistics about the worker thread.    string debug_status()    {    return ("Thread:\n"    " Handled works: "+handled+"\n"+    (handled?    " Average time: "+((int)(total_time / handled))+"ms\n"    " Max time: "+sprintf("%2.2fms\n", max_time/1000.0):"")+    " Status: "+(job?"Working":"Idle" )+"\n"+    (job?    (" "+
pike.git/lib/modules/Thread.pmod:598: Inside #if constant(thread_create)
   protected Handler aquire_thread()    {    object lock = mutex->lock();    while( !sizeof(free_threads) )    {    if( sizeof(threads) < max_num_threads )    {    threads += ({ Handler() });    free_threads += ({ threads[-1] });    } else { -  ft_cond->wait(mutex); +  ft_cond->wait(lock);    }    }    object(Handler) t = free_threads[0];    free_threads = free_threads[1..];    return t;    }          protected void dispatcher()    {    while( array q = [array]job_queue->read() )    aquire_thread()->run( q[1], q[0] );    }       protected class ValueAdjuster( object r, object r2, int i, mapping v )    {    void go(mixed vn, int err)    { -  +  if (!r->status()) {    ([array]r->value)[ i ] = vn;    if( err )    r->provide_error( err );    if( !--v->num_left )    r->provide( r->value ); -  +  }    destruct();    }    }    -  object run_multiple( array fun_args ) +  +  //! Register multiple jobs. +  //! +  //! @param fun_args +  //! An array of arrays where the first element +  //! is a function to call, and the second is +  //! a corresponding array of arguments. +  //! +  //! @returns +  //! Returns a @[Result] object with an array +  //! with one element for the result for each +  //! of the functions in @[fun_args]. +  //! +  //! @note +  //! Do not modify the elements of @[fun_args] +  //! before the result is available. +  //! +  //! @note +  //! If any of the functions in @[fun_args] throws +  //! and error, all of the accululated results +  //! (if any) will be dropped from the result, and +  //! the first backtrace be provided. +  //! +  //! @seealso +  //! @[run_multiple_async()] +  Result run_multiple( array(array(function|array)) fun_args )    {    Result r = Result(); // private result..    r->value = allocate( sizeof( fun_args ) );    mapping nl = ([ "num_left":sizeof( fun_args ) ]);    for( int i=0; i<sizeof( fun_args ); i++ )    { -  Result r2 = Result(); +  Result r2 = Result();    r2->set_done_cb( ValueAdjuster( r, r2, i, nl )->go );    job_queue->write( ({ r2, fun_args[i] }) );    }    return r;    }       -  +  //! Register multiple jobs where the return values +  //! are to be ignored. +  //! +  //! @param fun_args +  //! An array of arrays where the first element +  //! is a function to call, and the second is +  //! a corresponding array of arguments. +  //! +  //! @note +  //! Do not modify the elements of @[fun_args] +  //! before the result is available. +  //! +  //! @seealso +  //! @[run_multiple()]    void run_multiple_async( array fun_args )    {    for( int i=0; i<sizeof( fun_args ); i++ )    job_queue->write( ({ 0, fun_args[i] }) );    }       -  object run( function f, mixed ... args ) +  //! Register a job for the thread farm. +  //! +  //! @param f +  //! Function to call with @@@[args] to +  //! perform the job. +  //! +  //! @param args +  //! The parameters for @[f]. +  //! +  //! @returns +  //! Returns a @[Result] object for the job. +  //! +  //! @note +  //! In Pike 7.8 and earlier this function +  //! was broken and returned a @[Result] +  //! object that wasn't connected to the job. +  //! +  //! @seealso +  //! @[run_async()] +  Result run( function f, mixed ... args )    { -  object ro = Result(); -  job_queue->write( ({ 0, ({f, args }) }) ); +  Result ro = Result(); +  job_queue->write( ({ ro, ({f, args }) }) );    return ro;    }    -  +  //! Register a job for the thread farm +  //! where the return value from @[f] is +  //! ignored. +  //! +  //! @param f +  //! Function to call with @@@[args] to +  //! perform the job. +  //! +  //! @param args +  //! The parameters for @[f]. +  //! +  //! @seealso +  //! @[run()]    void run_async( function f, mixed ... args )    {    job_queue->write( ({ 0, ({f, args }) }) );    }    -  int set_max_num_threads( int to ) +  //! Set the maximum number of worker threads +  //! that the thread farm may have. +  //! +  //! @param to +  //! The new maximum number. +  //! +  //! If there are more worker threads than @[to], +  //! the function will wait until enough threads +  //! have finished, so that the total is @[to] or less. +  //! +  //! The default maximum number of worker threads is @expr{20@}. +  int set_max_num_threads( int(1..) to )    {    int omnt = max_num_threads;    if( to <= 0 )    error("Illegal argument 1 to set_max_num_threads,"    "num_threads must be > 0\n");       max_num_threads = to;    while( sizeof( threads ) > max_num_threads )    {    object key = mutex->lock();    while( sizeof( free_threads ) )    free_threads[0]->cond->signal();    if( sizeof( threads ) > max_num_threads)    ft_cond->wait(key);    }    ft_cond->broadcast( );    return omnt;    }    -  +  //! Get some statistics for the thread farm.    string debug_status()    {    string res = sprintf("Thread farm\n"    " Max threads = %d\n"    " Current threads = %d\n"    " Working threads = %d\n"    " Jobs in queue = %d\n\n",    max_num_threads, sizeof(threads),    (sizeof(threads)-sizeof(free_threads)),    job_queue->size() );