pike.git / lib / modules / Thread.pmod

version» Context lines:

pike.git/lib/modules/Thread.pmod:1:   #pike __REAL_VERSION__    - #if constant(thread_create) + #if constant(__builtin.thread_id)   constant Thread=__builtin.thread_id;    - //! Create a new thread. - //! - //! @deprecated predef::Thread.Thread - optional __deprecated__ 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; -  +    optional constant all_threads = predef::all_threads; -  + optional constant get_thread_quanta = predef::get_thread_quanta; + optional constant set_thread_quatna = predef::set_thread_quanta;      constant THREAD_NOT_STARTED = __builtin.THREAD_NOT_STARTED;   constant THREAD_RUNNING = __builtin.THREAD_RUNNING;   constant THREAD_EXITED = __builtin.THREAD_EXITED;         //! @[Fifo] implements a fixed length first-in, first-out queue.   //! A fifo is a queue of values and is often used as a stream of data   //! between two threads.   //!
pike.git/lib/modules/Thread.pmod:233: 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:378: Inside #if constant(thread_create)
   if(w_ptr >= sizeof(buffer))    {    buffer=buffer[r_ptr..];    buffer+=allocate(sizeof(buffer)+1);    w_ptr-=r_ptr;    r_ptr=0;    }    buffer[w_ptr] = value;    w_ptr++;    int items = w_ptr - r_ptr; -  r_cond::signal(); +  // NB: The mutex MUST be released before the broadcast to work +  // around bugs in glibc 2.24 and earlier. This seems to +  // affect eg RHEL 7 (glibc 2.17). +  // cf https://sourceware.org/bugzilla/show_bug.cgi?id=13165    key=0; -  +  r_cond::broadcast();    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(); -  +  protected object dispatcher_thread; +  protected function(object, string:void) thread_name_cb; +  protected string thread_name_prefix;       //! An asynchronous result.    class Result    {    int ready;    mixed value;    function done_cb;       //! @returns    //! @int
pike.git/lib/modules/Thread.pmod:504: Inside #if constant(thread_create)
   Mutex job_mutex = Mutex();    Condition cond = Condition();    array(object|array(function|array)) job;    object thread;       float total_time;    int handled, max_time;       protected int ready;    +  void update_thread_name(int is_exiting) +  { +  if (thread_name_cb) { +  string th_name = +  !is_exiting && +  sprintf("%s Handler 0x%x", thread_name_prefix, thread->id_number()); +  thread_name_cb(thread, th_name); +  } +  } +     void handler()    {    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; -  +  q = 0;    if( st > max_time )    max_time = st;    ft_cond->broadcast(); -  } else { +  } else {    object lock = mutex->lock();    threads -= ({ this });    free_threads -= ({ this });    lock = 0; -  +  update_thread_name(1);    destruct();    return;    }    }    }       void run( array(function|array) what, object|void resobj )    {    while(!ready) sleep(0.1);    object key = job_mutex->lock();
pike.git/lib/modules/Thread.pmod:570: Inside #if constant(thread_create)
   (" "+    replace( describe_backtrace(thread->backtrace()),    "\n",    "\n ")):"")    +"\n\n");    }       protected void create()    {    thread = thread_create( handler ); +  update_thread_name(0);    }          protected string _sprintf( int f )    {    switch( f )    {    case 't':    return "Thread.Farm().Handler";    case 'O':
pike.git/lib/modules/Thread.pmod:600: 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() ) +  while( array q = [array]job_queue->read() ) {    aquire_thread()->run( q[1], q[0] ); -  +  q = 0;    } -  +  if (thread_name_cb) +  thread_name_cb(this_thread(), 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 )
pike.git/lib/modules/Thread.pmod:649: Inside #if constant(thread_create)
   //! 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 +  //! and error, all of the accumulated 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 ) ]);
pike.git/lib/modules/Thread.pmod:766: Inside #if constant(thread_create)
   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;    }    +  //! Provide a callback function to track names of threads created by the +  //! farm. +  //! +  //! @param cb +  //! The callback function. This will get invoked with the thread as the +  //! first parameter and the name as the second whenever a thread is +  //! created. When the same thread terminates the callback is invoked +  //! again with @[0] as the second parameter. Set @[cb] to @[0] to stop +  //! any previously registered callbacks from being called. +  //! +  //! @param prefix +  //! An optional name prefix to distinguish different farms. If not given +  //! a prefix will be generated automatically. +  void set_thread_name_cb(function(object, string:void) cb, void|string prefix) +  { +  thread_name_cb = cb; +  thread_name_prefix = +  cb && +  (prefix || sprintf("Thread.Farm 0x%x", dispatcher_thread->id_number())); +  +  // Give a name to all existing threads +  if (thread_name_cb) { +  thread_name_cb(dispatcher_thread, thread_name_prefix + " Dispatcher"); +  foreach (threads, Handler t) +  t->update_thread_name(0); +  } +  } +     //! 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)),
pike.git/lib/modules/Thread.pmod:790: Inside #if constant(thread_create)
   return res;    }       protected string _sprintf( int f )    {    return f=='O' && sprintf( "%O(/* %s */)", this_program, debug_status() );    }       protected void create()    { -  thread_create( dispatcher ); +  dispatcher_thread = thread_create( dispatcher );    }   }    -  + //! When this key is destroyed, the corresponding resource counter + //! will be decremented. + //! + //! @seealso + //! @[ResourceCount], @[MutexKey] + //! + optional class ResourceCountKey { +  +  private inherit __builtin.DestructImmediate; +  +  /*semi*/private ResourceCount parent; +  +  /*semi*/private void create(ResourceCount _parent) { +  parent = _parent; +  } +  +  /*semi*/private void _destruct() { +  MutexKey key = parent->_mutex->lock(); +  --parent->_count; +  parent->_cond->signal(); +  } + } +  + //! Implements an inverted-semaphore-like resource + //! counter. A thread can poll or perform a blocking wait for the + //! resource-count to drop below a certain @ref{level@}. + //! + //! @seealso + //! @[ResourceCountKey], @[Condition], @[Mutex] + optional class ResourceCount { +  /*semi*/final int _count; +  /*semi*/final Condition _cond = Condition(); +  /*semi*/final Mutex _mutex = Mutex(); +  +  //! @param level +  //! The maximum level that is considered drained. +  //! +  //! @returns +  //! True if the resource counter drops to equal or below @ref{level@}. +  /*semi*/final int(0..1) drained(void|int level) { +  return level >= _count; +  } +  +  //! Blocks until the resource-counter dips to max @ref{level@}. +  //! +  //! @param level +  //! The maximum level that is considered drained. +  /*semi*/final void wait_till_drained(void|int level) { +  MutexKey key = _mutex->lock(); +  while (_count > level) // Recheck before allowing further +  _cond->wait(key); +  } +  +  //! Increments the resource-counter. +  //! @returns +  //! A @[ResourceCountKey] to decrement the resource-counter again. +  /*semi*/final ResourceCountKey acquire() { +  MutexKey key = _mutex->lock(); +  _count++; +  return ResourceCountKey(this); +  } +  +  /*semi*/private string _sprintf(int type) { +  string res = UNDEFINED; +  switch(type) { +  case 'O': +  res = sprintf("Count: %d", _count); +  break; +  case 'd': +  res = sprintf("%d", _count); +  break; +  } +  return res; +  } + } +    #else /* !constant(thread_create) */      // Simulations of some of the classes for nonthreaded use.      /* Fallback implementation of Thread.Local */   optional class Local   {    protected mixed data;    mixed get() {return data;}    mixed set (mixed val) {return data = val;}
pike.git/lib/modules/Thread.pmod:818:   {    int `!()    {    // Should be destructed when the mutex is, but we can't pull that    // off. Try to simulate it as well as possible.    if (dec_locks) return 0;    destruct (this);    return 1;    }    -  protected void destroy() +  protected void _destruct()    {    if (dec_locks) dec_locks();    }   }      /* Fallback implementation of Thread.Mutex */   optional class Mutex   {    protected int locks = 0;    protected void dec_locks() {locks--;}