a580e12000-09-27Fredrik Hübinette (Hubbe) #pike __REAL_VERSION__
a20af62000-09-26Fredrik Hübinette (Hubbe) 
8379a12017-06-25Martin Nilsson #if constant(__builtin.thread_id)
271dde1999-12-14Martin Stjernholm constant Thread=__builtin.thread_id;
cb49dd1999-07-20Per Hedbor 
da34bc2000-11-06Per Hedbor 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;
8379a12017-06-25Martin Nilsson optional constant get_thread_quanta = predef::get_thread_quanta; optional constant set_thread_quatna = predef::set_thread_quanta;
8b05c92000-11-06Martin Stjernholm 
6f13822006-04-07Martin Stjernholm constant THREAD_NOT_STARTED = __builtin.THREAD_NOT_STARTED; constant THREAD_RUNNING = __builtin.THREAD_RUNNING; constant THREAD_EXITED = __builtin.THREAD_EXITED;
da34bc2000-11-06Per Hedbor 
56cd002001-10-28Martin Nilsson //! @[Fifo] implements a fixed length first-in, first-out queue.
a17d6c2000-12-12Henrik Grubbström (Grubba) //! A fifo is a queue of values and is often used as a stream of data //! between two threads. //! //! @seealso
56cd002001-10-28Martin Nilsson //! @[Queue]
a17d6c2000-12-12Henrik Grubbström (Grubba) //!
da34bc2000-11-06Per Hedbor optional class Fifo {
7f30a61997-02-27Fredrik Hübinette (Hubbe)  inherit Condition : r_cond;
d4bf591999-06-12Henrik Grubbström (Grubba)  inherit Condition : w_cond; inherit Mutex : lock;
3524712015-05-26Martin Nilsson 
37d0891998-05-27Fredrik Hübinette (Hubbe)  array buffer;
87e3691997-02-27Fredrik Hübinette (Hubbe)  int ptr, num;
37d0891998-05-27Fredrik Hübinette (Hubbe)  int read_tres, write_tres;
a17d6c2000-12-12Henrik Grubbström (Grubba)  //! This function returns the number of elements currently in the fifo. //! //! @seealso
56cd002001-10-28Martin Nilsson  //! @[read()], @[write()]
a17d6c2000-12-12Henrik Grubbström (Grubba)  //!
87e3691997-02-27Fredrik Hübinette (Hubbe)  int size() { return num; }
a17d6c2000-12-12Henrik Grubbström (Grubba) 
9eaf1d2008-06-28Martin Nilsson  protected final mixed read_unlocked()
d4bf591999-06-12Henrik Grubbström (Grubba)  {
b5faf32003-04-14Martin Stjernholm  mixed tmp=buffer[ptr];
d4bf591999-06-12Henrik Grubbström (Grubba)  buffer[ptr++] = 0; // Throw away any references. ptr%=sizeof(buffer); if(read_tres < sizeof(buffer))
609c0f1997-02-27Fredrik Hübinette (Hubbe)  {
d4bf591999-06-12Henrik Grubbström (Grubba)  if(num-- == read_tres) w_cond::broadcast(); }else{ num--;
0c973f2000-02-08Henrik Grubbström (Grubba)  w_cond::broadcast();
609c0f1997-02-27Fredrik Hübinette (Hubbe)  }
d4bf591999-06-12Henrik Grubbström (Grubba)  return tmp; }
37d0891998-05-27Fredrik Hübinette (Hubbe) 
b5faf32003-04-14Martin Stjernholm  //! This function retrieves a value from the fifo. Values will be //! returned in the order they were written. If there are no values //! present in the fifo the current thread will sleep until some other //! thread writes one.
5012582000-12-16Henrik Grubbström (Grubba)  //! //! @seealso
b5faf32003-04-14Martin Stjernholm  //! @[try_read()], @[read_array()], @[write()]
5012582000-12-16Henrik Grubbström (Grubba)  //!
b5faf32003-04-14Martin Stjernholm  mixed read()
37d0891998-05-27Fredrik Hübinette (Hubbe)  {
b5faf32003-04-14Martin Stjernholm  object key=lock::lock();
37d0891998-05-27Fredrik Hübinette (Hubbe)  while(!num) r_cond::wait(key);
b5faf32003-04-14Martin Stjernholm  mixed res = read_unlocked(); key = 0; return res; } //! This function retrieves a value from the fifo if there is any //! there. Values will be returned in the order they were written. //! If there are no values present in the fifo then @[UNDEFINED] //! will be returned. //! //! @seealso //! @[read()] //! mixed try_read() { if (!num) return UNDEFINED; object key=lock::lock(); if (!num) return UNDEFINED; mixed res = read_unlocked(); key = 0; return res; }
9eaf1d2008-06-28Martin Nilsson  protected final array read_all_unlocked()
b5faf32003-04-14Martin Stjernholm  { array ret; switch (num) { case 0: ret = ({}); break; case 1: ret=buffer[ptr..ptr]; buffer[ptr++] = 0; // Throw away any references. ptr%=sizeof(buffer); num = 0; w_cond::broadcast(); break; default: if (ptr+num < sizeof(buffer)) { ret = buffer[ptr..ptr+num-1]; } else { ret = buffer[ptr..]+buffer[..num-(sizeof(buffer)-ptr)-1]; } ptr=num=0; buffer=allocate(sizeof(buffer)); // Throw away any references. w_cond::broadcast(); break;
37d0891998-05-27Fredrik Hübinette (Hubbe)  }
b5faf32003-04-14Martin Stjernholm  return ret; } //! This function returns all values in the fifo as an array. The //! values in the array will be in the order they were written. If //! there are no values present in the fifo the current thread will //! sleep until some other thread writes one. //! //! @seealso //! @[read()], @[try_read_array()] //! array read_array() { object key=lock::lock(); while(!num) r_cond::wait(key); array ret = read_all_unlocked();
0c973f2000-02-08Henrik Grubbström (Grubba)  key = 0;
37d0891998-05-27Fredrik Hübinette (Hubbe)  return ret; }
a17d6c2000-12-12Henrik Grubbström (Grubba) 
b5faf32003-04-14Martin Stjernholm  //! This function returns all values in the fifo as an array but //! doesn't wait if there are no values there. The values in the //! array will be in the order they were written.
a17d6c2000-12-12Henrik Grubbström (Grubba)  //! //! @seealso
b5faf32003-04-14Martin Stjernholm  //! @[read_array()]
a17d6c2000-12-12Henrik Grubbström (Grubba)  //!
b5faf32003-04-14Martin Stjernholm  array try_read_array() { if (!num) return ({}); object key=lock::lock(); array ret = read_all_unlocked(); key = 0; return ret; }
9eaf1d2008-06-28Martin Nilsson  protected final void write_unlocked (mixed value)
d4bf591999-06-12Henrik Grubbström (Grubba)  {
a17d6c2000-12-12Henrik Grubbström (Grubba)  buffer[(ptr + num) % sizeof(buffer)] = value;
d4bf591999-06-12Henrik Grubbström (Grubba)  if(write_tres)
609c0f1997-02-27Fredrik Hübinette (Hubbe)  {
d4bf591999-06-12Henrik Grubbström (Grubba)  if(num++ == write_tres) r_cond::broadcast(); }else{ num++;
0c973f2000-02-08Henrik Grubbström (Grubba)  r_cond::broadcast();
609c0f1997-02-27Fredrik Hübinette (Hubbe)  }
b5faf32003-04-14Martin Stjernholm  } //! Append a @[value] to the end of the fifo. If there is no more //! room in the fifo the current thread will sleep until space is //! available. The number of items in the queue after the write is //! returned. //! //! @seealso //! @[read()] //! int write(mixed value) { object key=lock::lock(); while(num == sizeof(buffer)) w_cond::wait(key); write_unlocked (value); int items = num;
0c973f2000-02-08Henrik Grubbström (Grubba)  key = 0;
b5faf32003-04-14Martin Stjernholm  return items; } //! Append a @[value] to the end of the fifo. If there is no more //! room in the fifo then zero will be returned, otherwise the //! number of items in the fifo after the write is returned. //! //! @seealso //! @[read()] //! int try_write(mixed value) { if (num == sizeof (buffer)) return 0; object key=lock::lock(); if (num == sizeof (buffer)) return 0; write_unlocked (value); int items = num; key = 0; return items;
d4bf591999-06-12Henrik Grubbström (Grubba)  }
37d0891998-05-27Fredrik Hübinette (Hubbe) 
a17d6c2000-12-12Henrik Grubbström (Grubba)  //! @decl void create() //! @decl void create(int size) //! //! Create a fifo. If the optional @[size] argument is present it //! sets how many values can be written to the fifo without blocking. //! The default @[size] is 128. //!
9eaf1d2008-06-28Martin Nilsson  protected void create(int|void size)
d4bf591999-06-12Henrik Grubbström (Grubba)  { write_tres=0; buffer=allocate(read_tres=size || 128); }
da34bc2000-11-06Per Hedbor 
9eaf1d2008-06-28Martin Nilsson  protected string _sprintf( int f )
da34bc2000-11-06Per Hedbor  {
ce3fd32002-11-29Martin Nilsson  return f=='O' && sprintf( "%O(%d / %d)", this_program, size(), read_tres );
da34bc2000-11-06Per Hedbor  }
ce3fd32002-11-29Martin Nilsson }
609c0f1997-02-27Fredrik Hübinette (Hubbe) 
56cd002001-10-28Martin Nilsson //! @[Queue] implements a queue, or a pipeline. The main difference //! between @[Queue] and @[Fifo] is that @[Queue]
a17d6c2000-12-12Henrik Grubbström (Grubba) //! will never block in write(), only allocate more memory. //!
8fe3fc2014-08-07Henrik Grubbström (Grubba) //! @fixme //! Ought to be made API-compatible with @[ADT.Queue]. //!
a17d6c2000-12-12Henrik Grubbström (Grubba) //! @seealso
8fe3fc2014-08-07Henrik Grubbström (Grubba) //! @[Fifo], @[ADT.Queue]
a17d6c2000-12-12Henrik Grubbström (Grubba) //!
da34bc2000-11-06Per Hedbor optional class Queue {
d4bf591999-06-12Henrik Grubbström (Grubba)  inherit Condition : r_cond; inherit Mutex : lock;
3524712015-05-26Martin Nilsson 
9626782000-02-18Martin Nilsson  array buffer=allocate(16);
609c0f1997-02-27Fredrik Hübinette (Hubbe)  int r_ptr, w_ptr;
3524712015-05-26Martin Nilsson 
a17d6c2000-12-12Henrik Grubbström (Grubba)  //! This function returns the number of elements currently in the queue. //! //! @seealso
56cd002001-10-28Martin Nilsson  //! @[read()], @[write()]
a17d6c2000-12-12Henrik Grubbström (Grubba)  //!
609c0f1997-02-27Fredrik Hübinette (Hubbe)  int size() { return w_ptr - r_ptr; }
a17d6c2000-12-12Henrik Grubbström (Grubba)  //! This function retrieves a value from the queue. Values will be //! returned in the order they were written. If there are no values //! present in the queue the current thread will sleep until some other
b5faf32003-04-14Martin Stjernholm  //! thread writes one.
a17d6c2000-12-12Henrik Grubbström (Grubba)  //! //! @seealso
b5faf32003-04-14Martin Stjernholm  //! @[try_read()], @[write()]
a17d6c2000-12-12Henrik Grubbström (Grubba)  //!
609c0f1997-02-27Fredrik Hübinette (Hubbe)  mixed read()
d4bf591999-06-12Henrik Grubbström (Grubba)  { mixed tmp;
a17d6c2000-12-12Henrik Grubbström (Grubba)  object key=lock::lock();
b5faf32003-04-14Martin Stjernholm  while(w_ptr == r_ptr) r_cond::wait(key);
d4bf591999-06-12Henrik Grubbström (Grubba)  tmp=buffer[r_ptr]; buffer[r_ptr++] = 0; // Throw away any references. key=0; return tmp; }
a17d6c2000-12-12Henrik Grubbström (Grubba) 
b5faf32003-04-14Martin Stjernholm  //! This function retrieves a value from the queue if there is any //! there. Values will be returned in the order they were written. //! If there are no values present in the fifo then @[UNDEFINED] //! will be returned. //! //! @seealso //! @[write()] //! mixed try_read() { if (w_ptr == r_ptr) return UNDEFINED; object key=lock::lock(); if (w_ptr == r_ptr) return UNDEFINED; mixed tmp=buffer[r_ptr]; buffer[r_ptr++] = 0; // Throw away any references. key=0; return tmp; }
9eaf1d2008-06-28Martin Nilsson  protected final array read_all_unlocked()
b5faf32003-04-14Martin Stjernholm  { array ret; switch (w_ptr - r_ptr) { case 0: ret = ({}); break; case 1: ret=buffer[r_ptr..r_ptr]; buffer[r_ptr++] = 0; // Throw away any references. break; default:
fc1da92010-09-18Artur Skawina  ret = buffer[r_ptr..w_ptr-1];
b5faf32003-04-14Martin Stjernholm  r_ptr = w_ptr = 0; buffer=allocate(sizeof(buffer)); // Throw away any references. break; } return ret; } //! This function returns all values in the queue as an array. The //! values in the array will be in the order they were written. If //! there are no values present in the queue the current thread will //! sleep until some other thread writes one. //! //! @seealso //! @[read()], @[try_read_array()] //! array read_array() { object key=lock::lock(); while (w_ptr == r_ptr) r_cond::wait(key); array ret = read_all_unlocked(); key = 0; return ret; } //! This function returns all values in the queue as an array but //! doesn't wait if there are no values there. The values in the //! array will be in the order they were written. //! //! @seealso //! @[read_array()] //! array try_read_array() { if (w_ptr == r_ptr) return ({}); object key=lock::lock(); array ret = read_all_unlocked(); key = 0; return ret; }
3ad3a42010-11-10Martin Stjernholm  //! Returns a snapshot of all the values in the queue, in the order //! they were written. The values are still left in the queue, so if //! other threads are reading from it, the returned value should be //! considered stale already on return. array peek_array() { if (w_ptr == r_ptr) return ({}); MutexKey key = lock::lock(); array ret = buffer[r_ptr..w_ptr - 1]; key = 0; return ret; }
a17d6c2000-12-12Henrik Grubbström (Grubba)  //! This function puts a @[value] last in the queue. If the queue is
b5faf32003-04-14Martin Stjernholm  //! too small to hold the @[value] it will be expanded to make room. //! The number of items in the queue after the write is returned.
a17d6c2000-12-12Henrik Grubbström (Grubba)  //! //! @seealso
56cd002001-10-28Martin Nilsson  //! @[read()]
a17d6c2000-12-12Henrik Grubbström (Grubba)  //!
b5faf32003-04-14Martin Stjernholm  int write(mixed value)
d4bf591999-06-12Henrik Grubbström (Grubba)  {
a17d6c2000-12-12Henrik Grubbström (Grubba)  object key=lock::lock();
d4bf591999-06-12Henrik Grubbström (Grubba)  if(w_ptr >= sizeof(buffer))
609c0f1997-02-27Fredrik Hübinette (Hubbe)  {
d4bf591999-06-12Henrik Grubbström (Grubba)  buffer=buffer[r_ptr..]; buffer+=allocate(sizeof(buffer)+1); w_ptr-=r_ptr; r_ptr=0;
609c0f1997-02-27Fredrik Hübinette (Hubbe)  }
a17d6c2000-12-12Henrik Grubbström (Grubba)  buffer[w_ptr] = value;
d4bf591999-06-12Henrik Grubbström (Grubba)  w_ptr++;
b5faf32003-04-14Martin Stjernholm  int items = w_ptr - r_ptr;
d4bf591999-06-12Henrik Grubbström (Grubba)  r_cond::signal();
b5faf32003-04-14Martin Stjernholm  key=0; return items;
d4bf591999-06-12Henrik Grubbström (Grubba)  }
da34bc2000-11-06Per Hedbor 
9eaf1d2008-06-28Martin Nilsson  protected string _sprintf( int f )
da34bc2000-11-06Per Hedbor  {
ce3fd32002-11-29Martin Nilsson  return f=='O' && sprintf( "%O(%d)", this_program, size() );
da34bc2000-11-06Per Hedbor  }
cb49dd1999-07-20Per Hedbor }
d8a00f2013-07-05Henrik Grubbström (Grubba) //! A thread farm.
da34bc2000-11-06Per Hedbor optional class Farm
cb49dd1999-07-20Per Hedbor {
9eaf1d2008-06-28Martin Nilsson  protected Mutex mutex = Mutex(); protected Condition ft_cond = Condition(); protected Queue job_queue = Queue();
9c0bc12018-04-04Jonas Walldén  protected object dispatcher_thread; protected function(object, string:void) thread_name_cb; protected string thread_name_prefix;
69a5b52002-10-01Henrik Grubbström (Grubba) 
d8a00f2013-07-05Henrik Grubbström (Grubba)  //! An asynchronous result.
cb49dd1999-07-20Per Hedbor  class Result { int ready; mixed value; function done_cb;
d8a00f2013-07-05Henrik Grubbström (Grubba)  //! @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
cb49dd1999-07-20Per Hedbor  int status() { return ready; }
d8a00f2013-07-05Henrik Grubbström (Grubba)  //! @returns //! Returns the result if available, a backtrace on failure, //! and @expr{0@} (zero) otherwise.
cb49dd1999-07-20Per Hedbor  mixed result() { return value; }
d8a00f2013-07-05Henrik Grubbström (Grubba)  //! Wait for completion.
cb49dd1999-07-20Per Hedbor  mixed `()() {
69a5b52002-10-01Henrik Grubbström (Grubba)  object key = mutex->lock(); while(!ready) ft_cond->wait(key); key = 0;
cb49dd1999-07-20Per Hedbor  if( ready < 0 ) throw( value ); return value; }
d8a00f2013-07-05Henrik Grubbström (Grubba)  //! 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.
cb49dd1999-07-20Per Hedbor  void set_done_cb( function to ) { if( ready ) to( value, ready<0 ); else done_cb = to; }
d8a00f2013-07-05Henrik Grubbström (Grubba)  //! Register a failure. //! //! @param what //! The corresponding backtrace.
cb49dd1999-07-20Per Hedbor  void provide_error( mixed what ) { value = what; ready = -1; if( done_cb ) done_cb( what, 1 ); }
d8a00f2013-07-05Henrik Grubbström (Grubba)  //! Register a completed result. //! //! @param what //! The result to register.
cb49dd1999-07-20Per Hedbor  void provide( mixed what ) { ready = 1; value = what; if( done_cb ) done_cb( what, 0 ); }
da34bc2000-11-06Per Hedbor 
9eaf1d2008-06-28Martin Nilsson  protected string _sprintf( int f )
da34bc2000-11-06Per Hedbor  { switch( f ) { case 't': return "Thread.Farm().Result"; case 'O':
2040342004-01-11Martin Nilsson  return sprintf( "%t(%d %O)", this, ready, value );
da34bc2000-11-06Per Hedbor  } }
cb49dd1999-07-20Per Hedbor  }
d8a00f2013-07-05Henrik Grubbström (Grubba)  //! A worker thread.
9eaf1d2008-06-28Martin Nilsson  protected class Handler
cb49dd1999-07-20Per Hedbor  {
69a5b52002-10-01Henrik Grubbström (Grubba)  Mutex job_mutex = Mutex();
cb49dd1999-07-20Per Hedbor  Condition cond = Condition();
adaf462000-01-31Henrik Grubbström (Grubba)  array(object|array(function|array)) job;
cb49dd1999-07-20Per Hedbor  object thread; float total_time; int handled, max_time;
9eaf1d2008-06-28Martin Nilsson  protected int ready;
cb49dd1999-07-20Per Hedbor 
9c0bc12018-04-04Jonas Walldén  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); } }
cb49dd1999-07-20Per Hedbor  void handler() {
adaf462000-01-31Henrik Grubbström (Grubba)  array(object|array(function|array)) q;
69a5b52002-10-01Henrik Grubbström (Grubba)  object key = job_mutex->lock(); ready = 1;
cb49dd1999-07-20Per Hedbor  while( 1 ) {
69a5b52002-10-01Henrik Grubbström (Grubba)  cond->wait(key);
cb49dd1999-07-20Per Hedbor  if( q = job ) { mixed res, err; int st = gethrtime();
d892a32015-04-21Arne Goedeke  err = catch(res = q[1][0]( @q[1][1] )); if( q[0] ) { if( err ) ([object]q[0])->provide_error( err ); else ([object]q[0])->provide( res ); }
cb49dd1999-07-20Per Hedbor  object lock = mutex->lock();
2040342004-01-11Martin Nilsson  free_threads += ({ this });
cb49dd1999-07-20Per Hedbor  lock = 0; st = gethrtime()-st; total_time += st/1000.0; handled++; job = 0;
a1063a2018-06-28Jonas Walldén  q = 0;
cb49dd1999-07-20Per Hedbor  if( st > max_time ) max_time = st; ft_cond->broadcast();
9c0bc12018-04-04Jonas Walldén  } else {
f08d361999-08-04Per Hedbor  object lock = mutex->lock();
2040342004-01-11Martin Nilsson  threads -= ({ this }); free_threads -= ({ this });
f08d361999-08-04Per Hedbor  lock = 0;
9c0bc12018-04-04Jonas Walldén  update_thread_name(1);
f08d361999-08-04Per Hedbor  destruct(); return;
cb49dd1999-07-20Per Hedbor  } } }
adaf462000-01-31Henrik Grubbström (Grubba)  void run( array(function|array) what, object|void resobj )
cb49dd1999-07-20Per Hedbor  { while(!ready) sleep(0.1);
69a5b52002-10-01Henrik Grubbström (Grubba)  object key = job_mutex->lock();
cb49dd1999-07-20Per Hedbor  job = ({ resobj, what }); cond->signal();
69a5b52002-10-01Henrik Grubbström (Grubba)  key = 0;
cb49dd1999-07-20Per Hedbor  }
d8a00f2013-07-05Henrik Grubbström (Grubba)  //! Get some statistics about the worker thread.
cb49dd1999-07-20Per Hedbor  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? (" "+ replace( describe_backtrace(thread->backtrace()), "\n", "\n ")):"") +"\n\n"); }
9eaf1d2008-06-28Martin Nilsson  protected void create()
cb49dd1999-07-20Per Hedbor  { thread = thread_create( handler );
9c0bc12018-04-04Jonas Walldén  update_thread_name(0);
cb49dd1999-07-20Per Hedbor  }
da34bc2000-11-06Per Hedbor 
9eaf1d2008-06-28Martin Nilsson  protected string _sprintf( int f )
da34bc2000-11-06Per Hedbor  { switch( f ) { case 't': return "Thread.Farm().Handler"; case 'O':
2040342004-01-11Martin Nilsson  return sprintf( "%t(%f / %d, %d)", this,
ce3fd32002-11-29Martin Nilsson  total_time, max_time, handled );
da34bc2000-11-06Per Hedbor  } }
cb49dd1999-07-20Per Hedbor  }
9eaf1d2008-06-28Martin Nilsson  protected array(Handler) threads = ({}); protected array(Handler) free_threads = ({}); protected int max_num_threads = 20;
cb49dd1999-07-20Per Hedbor 
9eaf1d2008-06-28Martin Nilsson  protected Handler aquire_thread()
cb49dd1999-07-20Per Hedbor  { object lock = mutex->lock(); while( !sizeof(free_threads) ) { if( sizeof(threads) < max_num_threads ) { threads += ({ Handler() }); free_threads += ({ threads[-1] }); } else {
7ed3b62014-01-16Arne Goedeke  ft_cond->wait(lock);
cb49dd1999-07-20Per Hedbor  } }
adaf462000-01-31Henrik Grubbström (Grubba)  object(Handler) t = free_threads[0];
cb49dd1999-07-20Per Hedbor  free_threads = free_threads[1..]; return t; }
3524712015-05-26Martin Nilsson 
cb49dd1999-07-20Per Hedbor 
9eaf1d2008-06-28Martin Nilsson  protected void dispatcher()
cb49dd1999-07-20Per Hedbor  {
a1063a2018-06-28Jonas Walldén  while( array q = [array]job_queue->read() ) {
cb49dd1999-07-20Per Hedbor  aquire_thread()->run( q[1], q[0] );
a1063a2018-06-28Jonas Walldén  q = 0; }
9c0bc12018-04-04Jonas Walldén  if (thread_name_cb) thread_name_cb(this_thread(), 0);
cb49dd1999-07-20Per Hedbor  }
9eaf1d2008-06-28Martin Nilsson  protected class ValueAdjuster( object r, object r2, int i, mapping v )
cb49dd1999-07-20Per Hedbor  { void go(mixed vn, int err) {
d8a00f2013-07-05Henrik Grubbström (Grubba)  if (!r->status()) { ([array]r->value)[ i ] = vn; if( err ) r->provide_error( err ); if( !--v->num_left ) r->provide( r->value ); }
cb49dd1999-07-20Per Hedbor  destruct(); } }
d8a00f2013-07-05Henrik Grubbström (Grubba)  //! 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
89f3cd2016-03-30Peter Bortas  //! and error, all of the accumulated results
d8a00f2013-07-05Henrik Grubbström (Grubba)  //! (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 )
cb49dd1999-07-20Per Hedbor  { 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++ ) {
d8a00f2013-07-05Henrik Grubbström (Grubba)  Result r2 = Result();
cb49dd1999-07-20Per Hedbor  r2->set_done_cb( ValueAdjuster( r, r2, i, nl )->go ); job_queue->write( ({ r2, fun_args[i] }) ); } return r; }
f08d361999-08-04Per Hedbor 
d8a00f2013-07-05Henrik Grubbström (Grubba)  //! 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()]
f08d361999-08-04Per Hedbor  void run_multiple_async( array fun_args ) { for( int i=0; i<sizeof( fun_args ); i++ ) job_queue->write( ({ 0, fun_args[i] }) ); }
d8a00f2013-07-05Henrik Grubbström (Grubba)  //! 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 )
f08d361999-08-04Per Hedbor  {
d8a00f2013-07-05Henrik Grubbström (Grubba)  Result ro = Result(); job_queue->write( ({ ro, ({f, args }) }) );
f08d361999-08-04Per Hedbor  return ro; }
d8a00f2013-07-05Henrik Grubbström (Grubba)  //! 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()]
cb49dd1999-07-20Per Hedbor  void run_async( function f, mixed ... args ) { job_queue->write( ({ 0, ({f, args }) }) ); }
d8a00f2013-07-05Henrik Grubbström (Grubba)  //! 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 )
cb49dd1999-07-20Per Hedbor  { int omnt = max_num_threads;
f08d361999-08-04Per Hedbor  if( to <= 0 ) error("Illegal argument 1 to set_max_num_threads," "num_threads must be > 0\n");
cb49dd1999-07-20Per Hedbor  max_num_threads = to;
f08d361999-08-04Per Hedbor  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)
69a5b52002-10-01Henrik Grubbström (Grubba)  ft_cond->wait(key);
f08d361999-08-04Per Hedbor  }
cb49dd1999-07-20Per Hedbor  ft_cond->broadcast( ); return omnt; }
9c0bc12018-04-04Jonas Walldén  //! 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); } }
d8a00f2013-07-05Henrik Grubbström (Grubba)  //! Get some statistics for the thread farm.
cb49dd1999-07-20Per Hedbor  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",
3524712015-05-26Martin Nilsson  max_num_threads, sizeof(threads),
cb49dd1999-07-20Per Hedbor  (sizeof(threads)-sizeof(free_threads)), job_queue->size() );
3524712015-05-26Martin Nilsson 
cb49dd1999-07-20Per Hedbor  foreach( threads, Handler t ) res += t->debug_status(); return res; }
9eaf1d2008-06-28Martin Nilsson  protected string _sprintf( int f )
da34bc2000-11-06Per Hedbor  {
ce3fd32002-11-29Martin Nilsson  return f=='O' && sprintf( "%O(/* %s */)", this_program, debug_status() );
da34bc2000-11-06Per Hedbor  }
9eaf1d2008-06-28Martin Nilsson  protected void create()
cb49dd1999-07-20Per Hedbor  {
9c0bc12018-04-04Jonas Walldén  dispatcher_thread = thread_create( dispatcher );
cb49dd1999-07-20Per Hedbor  } }
38906d2000-11-19Martin Stjernholm 
cebbeb2017-11-10Stephen R. van den Berg //! When this key is destroyed, the corresponding resource counter //! will be decremented. //! //! @seealso //! @[ResourceCount], @[MutexKey] //! optional class ResourceCountKey {
6a66f82018-08-05Marcus Comstedt  private inherit __builtin.DestructImmediate;
cebbeb2017-11-10Stephen R. van den Berg  /*semi*/private ResourceCount parent; /*semi*/private void create(ResourceCount _parent) { parent = _parent; } /*semi*/private void _destruct() {
d168eb2018-08-05Henrik Grubbström (Grubba)  MutexKey key = parent->_mutex->lock();
cebbeb2017-11-10Stephen R. van den Berg  --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();
62f1ba2018-05-08Stephen R. van den Berg  /*semi*/final Mutex _mutex = Mutex();
cebbeb2017-11-10Stephen R. van den Berg  //! @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.
62f1ba2018-05-08Stephen R. van den Berg  /*semi*/final void wait_till_drained(void|int level) {
d168eb2018-08-05Henrik Grubbström (Grubba)  MutexKey key = _mutex->lock();
cebbeb2017-11-10Stephen R. van den Berg  while (_count > level) // Recheck before allowing further
d168eb2018-08-05Henrik Grubbström (Grubba)  _cond->wait(key);
cebbeb2017-11-10Stephen R. van den Berg  } //! Increments the resource-counter. //! @returns //! A @[ResourceCountKey] to decrement the resource-counter again. /*semi*/final ResourceCountKey acquire() {
d168eb2018-08-05Henrik Grubbström (Grubba)  MutexKey key = _mutex->lock();
cebbeb2017-11-10Stephen R. van den Berg  _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; } }
38906d2000-11-19Martin Stjernholm #else /* !constant(thread_create) */ // Simulations of some of the classes for nonthreaded use.
914c942001-02-01Henrik Grubbström (Grubba) /* Fallback implementation of Thread.Local */
4e692e2003-07-24Henrik Grubbström (Grubba) optional class Local
38906d2000-11-19Martin Stjernholm {
9eaf1d2008-06-28Martin Nilsson  protected mixed data;
38906d2000-11-19Martin Stjernholm  mixed get() {return data;} mixed set (mixed val) {return data = val;} }
914c942001-02-01Henrik Grubbström (Grubba) /* Fallback implementation of Thread.MutexKey */
9eaf1d2008-06-28Martin Nilsson optional class MutexKey (protected function(:void) dec_locks)
38906d2000-11-19Martin Stjernholm { 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;
2040342004-01-11Martin Nilsson  destruct (this);
38906d2000-11-19Martin Stjernholm  return 1; }
c071bc2017-11-05Henrik Grubbström (Grubba)  protected void _destruct()
38906d2000-11-19Martin Stjernholm  { if (dec_locks) dec_locks(); } }
914c942001-02-01Henrik Grubbström (Grubba) /* Fallback implementation of Thread.Mutex */
4e692e2003-07-24Henrik Grubbström (Grubba) optional class Mutex
38906d2000-11-19Martin Stjernholm {
9eaf1d2008-06-28Martin Nilsson  protected int locks = 0; protected void dec_locks() {locks--;}
38906d2000-11-19Martin Stjernholm 
a17d6c2000-12-12Henrik Grubbström (Grubba)  MutexKey lock (int|void type)
38906d2000-11-19Martin Stjernholm  { switch (type) { default: error ("Unknown mutex locking style: %d\n", type); case 0: if (locks) error ("Recursive mutex locks.\n"); break; case 1: if (locks) // To be really accurate we should hang now, but somehow // that doesn't seem too useful. error ("Deadlock detected.\n");
070c1c2001-09-27Martin Stjernholm  break; case 2:
8715202001-09-27Martin Stjernholm  if (locks) return 0;
38906d2000-11-19Martin Stjernholm  } locks++; return MutexKey (dec_locks); }
a17d6c2000-12-12Henrik Grubbström (Grubba)  MutexKey trylock (int|void type)
38906d2000-11-19Martin Stjernholm  { switch (type) { default: error ("Unknown mutex locking style: %d\n", type); case 0: if (locks) error ("Recursive mutex locks.\n"); break; case 1: case 2: } if (locks) return 0; locks++; return MutexKey (dec_locks); } }
b5faf32003-04-14Martin Stjernholm // Fallback implementation of Thread.Fifo.
4e692e2003-07-24Henrik Grubbström (Grubba) optional class Fifo
b5faf32003-04-14Martin Stjernholm { array buffer; int ptr, num; int read_tres, write_tres; int size() { return num; } mixed read() { if (!num) error ("Deadlock detected - fifo empty.\n"); return try_read(); } mixed try_read() { if (!num) return UNDEFINED; mixed tmp=buffer[ptr]; buffer[ptr++] = 0; // Throw away any references. ptr%=sizeof(buffer); return tmp; } array read_array() { if (!num) error ("Deadlock detected - fifo empty.\n"); return try_read_array(); } array try_read_array() {
1595bb2003-05-07Henrik Grubbström (Grubba)  array ret;
b5faf32003-04-14Martin Stjernholm  switch (num) { case 0: ret = ({}); break; case 1: ret=buffer[ptr..ptr]; buffer[ptr++] = 0; // Throw away any references. ptr%=sizeof(buffer); num = 0; break; default: if (ptr+num < sizeof(buffer)) { ret = buffer[ptr..ptr+num-1]; } else { ret = buffer[ptr..]+buffer[..num-(sizeof(buffer)-ptr)-1]; } ptr=num=0; buffer=allocate(sizeof(buffer)); // Throw away any references. break; } return ret; } int try_write(mixed value) { if (num == sizeof (buffer)) return 0; buffer[(ptr + num) % sizeof(buffer)] = value; return ++num; }
d932e62003-05-16Henrik Grubbström (Grubba)  int write(mixed value) { if (!try_write(value)) error("Deadlock detected - fifo full.\n"); return num; }
9eaf1d2008-06-28Martin Nilsson  protected void create(int|void size)
b5faf32003-04-14Martin Stjernholm  { write_tres=0; buffer=allocate(read_tres=size || 128); }
9eaf1d2008-06-28Martin Nilsson  protected string _sprintf( int f )
b5faf32003-04-14Martin Stjernholm  { return f=='O' && sprintf( "%O(%d / %d)", this_program, size(), read_tres ); } } // Fallback implementation of Thread.Queue.
4e692e2003-07-24Henrik Grubbström (Grubba) optional class Queue
b5faf32003-04-14Martin Stjernholm { array buffer=allocate(16); int r_ptr, w_ptr;
3524712015-05-26Martin Nilsson 
b5faf32003-04-14Martin Stjernholm  int size() { return w_ptr - r_ptr; } mixed read() { if (w_ptr == r_ptr) error ("Deadlock detected - queue empty.\n"); return try_read(); } mixed try_read() { mixed tmp=buffer[r_ptr]; buffer[r_ptr++] = 0; // Throw away any references. return tmp; } array read_array() { if (w_ptr == r_ptr) error ("Deadlock detected - queue empty.\n"); return try_read_array(); } array try_read_array() { array ret; switch (w_ptr - r_ptr) { case 0: ret = ({}); break; case 1: ret=buffer[r_ptr..r_ptr]; buffer[r_ptr++] = 0; // Throw away any references. break; default:
fc1da92010-09-18Artur Skawina  ret = buffer[r_ptr..w_ptr-1];
b5faf32003-04-14Martin Stjernholm  r_ptr = w_ptr = 0; buffer=allocate(sizeof(buffer)); // Throw away any references. break; } return ret; } int write(mixed value) { 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++; return w_ptr - r_ptr; }
9eaf1d2008-06-28Martin Nilsson  protected string _sprintf( int f )
b5faf32003-04-14Martin Stjernholm  { return f=='O' && sprintf( "%O(%d)", this_program, size() ); } }
38906d2000-11-19Martin Stjernholm #endif /* !constant(thread_create) */