[DRE-commits] r2118 - in packages-wip/libfastthread-ruby/trunk: . debian

micah at alioth.debian.org micah at alioth.debian.org
Fri Dec 7 21:39:56 UTC 2007


Author: micah
Date: 2007-12-07 21:39:56 +0000 (Fri, 07 Dec 2007)
New Revision: 2118

Added:
   packages-wip/libfastthread-ruby/trunk/debian/
   packages-wip/libfastthread-ruby/trunk/debian/changelog
   packages-wip/libfastthread-ruby/trunk/debian/compat
   packages-wip/libfastthread-ruby/trunk/debian/control
   packages-wip/libfastthread-ruby/trunk/debian/copyright
   packages-wip/libfastthread-ruby/trunk/debian/rules
   packages-wip/libfastthread-ruby/trunk/debian/watch
   packages-wip/libfastthread-ruby/trunk/extconf.rb
   packages-wip/libfastthread-ruby/trunk/fastthread.c
Log:
[svn-inject] Applying Debian modifications to trunk

Added: packages-wip/libfastthread-ruby/trunk/debian/changelog
===================================================================
--- packages-wip/libfastthread-ruby/trunk/debian/changelog	                        (rev 0)
+++ packages-wip/libfastthread-ruby/trunk/debian/changelog	2007-12-07 21:39:56 UTC (rev 2118)
@@ -0,0 +1,5 @@
+libfastthread-ruby (1.0.1-1) unstable; urgency=low
+
+  * Initial Debian release
+
+ -- Micah Anderson <micah at debian.org>  Thu, 06 Dec 2007 23:19:01 -0500

Added: packages-wip/libfastthread-ruby/trunk/debian/compat
===================================================================
--- packages-wip/libfastthread-ruby/trunk/debian/compat	                        (rev 0)
+++ packages-wip/libfastthread-ruby/trunk/debian/compat	2007-12-07 21:39:56 UTC (rev 2118)
@@ -0,0 +1 @@
+5

Added: packages-wip/libfastthread-ruby/trunk/debian/control
===================================================================
--- packages-wip/libfastthread-ruby/trunk/debian/control	                        (rev 0)
+++ packages-wip/libfastthread-ruby/trunk/debian/control	2007-12-07 21:39:56 UTC (rev 2118)
@@ -0,0 +1,30 @@
+Source: libfastthread-ruby
+Section: libs
+Priority: optional
+Maintainer: Micah Anderson <micah at debian.org>
+Uploaders: Debian Ruby Extras Maintainers <pkg-ruby-extras-maintainers at lists.alioth.debian.org>
+Build-Depends: cdbs, debhelper (>= 5), ruby-pkg-tools (>= 0.8), ruby1.8-dev
+Standards-Version: 3.7.3
+Homepage: http://moonbase.rydia.net/mental/blog/programming/fastthread
+
+Package: libfastthread-ruby
+Architecture: any
+Depends: libfastthread-ruby1.8
+Description: Optimized replacement for thread.rb primitives
+ A fastthread library which replaces the existing Mutex, ConditionVariable, 
+ Queue and SizedQueue classes with faster versions, giving any applications 
+ using the existing standard locking primitives a speed boost and a cure for 
+ memory leaks in stdlib versions of the same classes.
+ .
+ This is a dependency package which depends on Debian's default Ruby version
+ (currently 1.8).
+
+Package: libfastthread-ruby1.8
+Architecture: any
+Depends: ruby1.8, ${shlibs:Depends}, ${misc:Depends}
+Description: Optimized replacement for thread.rb primitives
+ A fastthread library which replaces the existing Mutex, ConditionVariable, 
+ Queue and SizedQueue classes with faster versions, giving any applications 
+ using the existing standard locking primitives a speed boost and a cure for 
+ memory leaks in stdlib versions of the same classes.
+

Added: packages-wip/libfastthread-ruby/trunk/debian/copyright
===================================================================
--- packages-wip/libfastthread-ruby/trunk/debian/copyright	                        (rev 0)
+++ packages-wip/libfastthread-ruby/trunk/debian/copyright	2007-12-07 21:39:56 UTC (rev 2118)
@@ -0,0 +1,78 @@
+This package was debianized by Micah Anderson <micah at debian.org> on
+Fri Dec  7 04:27:36 UTC 2007
+
+It was downloaded from http://rubyforge.org/frs/download.php/27041/fastthread-1.0.1.tgz
+
+Upstream Author: MenTaLguY and others
+
+Copyright (c) 2006-2007 MenTaLguY <mental at rydia.net>
+
+Optimized Ruby Mutex implementation (fastthread) is made available under
+the same terms as Ruby, reproduced below:
+
+You can redistribute it and/or modify it under either the terms of the GPL
+(see COPYING file), or the conditions below:
+
+  1. You may make and give away verbatim copies of the source form of the
+     software without restriction, provided that you duplicate all of the
+     original copyright notices and associated disclaimers.
+
+  2. You may modify your copy of the software in any way, provided that
+     you do at least ONE of the following:
+
+       a) place your modifications in the Public Domain or otherwise
+          make them Freely Available, such as by posting said
+	  modifications to Usenet or an equivalent medium, or by allowing
+	  the author to include your modifications in the software.
+
+       b) use the modified software only within your corporation or
+          organization.
+
+       c) rename any non-standard executables so the names do not conflict
+	  with standard executables, which must also be provided.
+
+       d) make other distribution arrangements with the author.
+
+  3. You may distribute the software in object code or executable
+     form, provided that you do at least ONE of the following:
+
+       a) distribute the executables and library files of the software,
+	  together with instructions (in the manual page or equivalent)
+	  on where to get the original distribution.
+
+       b) accompany the distribution with the machine-readable source of
+	  the software.
+
+       c) give non-standard executables non-standard names, with
+          instructions on where to get the original software distribution.
+
+       d) make other distribution arrangements with the author.
+
+  4. You may modify and include the part of the software into any other
+     software (possibly commercial).  But some files in the distribution
+     are not written by the author, so that they are not under this terms.
+     They are gc.c(partly), utils.c(partly), regex.[ch], fnmatch.[ch],
+     glob.c, st.[ch] and some files under the ./missing directory.  See
+     each file for the copying condition.
+
+  5. The scripts and library files supplied as input to or produced as 
+     output from the software do not automatically fall under the
+     copyright of the software, but belong to whomever generated them, 
+     and may be sold commercially, and may be aggregated with this
+     software.
+
+  6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+     IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+     WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+     PURPOSE.
+
+-- 
+On Debian GNU/Linux systems, the complete text of the GNU General
+Public License can be found in `/usr/share/common-licenses/GPL'.
+
+setup.rb is Copyright (c) 2000-2004 Minero Aoki and is distirbuted under the
+terms of the GNU LGPL, Lesser General Public License version 2.1. See
+`/usr/share/common-licenses/LGPL-2.1'.
+
+The Debian packaging is (C) 2007, Micah Anderson <micah at debian.org>
+and is licensed under the GPL, see `/usr/share/common-licenses/GPL'.

Added: packages-wip/libfastthread-ruby/trunk/debian/rules
===================================================================
--- packages-wip/libfastthread-ruby/trunk/debian/rules	                        (rev 0)
+++ packages-wip/libfastthread-ruby/trunk/debian/rules	2007-12-07 21:39:56 UTC (rev 2118)
@@ -0,0 +1,4 @@
+#!/usr/bin/make -f
+  
+include /usr/share/cdbs/1/rules/debhelper.mk
+include /usr/share/ruby-pkg-tools/1/class/ruby-extconf-rb.mk


Property changes on: packages-wip/libfastthread-ruby/trunk/debian/rules
___________________________________________________________________
Name: svn:executable
   + *

Added: packages-wip/libfastthread-ruby/trunk/debian/watch
===================================================================
--- packages-wip/libfastthread-ruby/trunk/debian/watch	                        (rev 0)
+++ packages-wip/libfastthread-ruby/trunk/debian/watch	2007-12-07 21:39:56 UTC (rev 2118)
@@ -0,0 +1,2 @@
+version=3
+http://rubyforge.org/frs/?group_id=1306 .*fastthread-(.*)\.t.*

Added: packages-wip/libfastthread-ruby/trunk/extconf.rb
===================================================================
--- packages-wip/libfastthread-ruby/trunk/extconf.rb	                        (rev 0)
+++ packages-wip/libfastthread-ruby/trunk/extconf.rb	2007-12-07 21:39:56 UTC (rev 2118)
@@ -0,0 +1,3 @@
+require 'mkmf'
+
+create_makefile('fastthread')

Added: packages-wip/libfastthread-ruby/trunk/fastthread.c
===================================================================
--- packages-wip/libfastthread-ruby/trunk/fastthread.c	                        (rev 0)
+++ packages-wip/libfastthread-ruby/trunk/fastthread.c	2007-12-07 21:39:56 UTC (rev 2118)
@@ -0,0 +1,1186 @@
+/*
+ * Optimized Ruby Mutex implementation, loosely based on thread.rb by
+ * Yukihiro Matsumoto <matz at ruby-lang.org>
+ *
+ *  Copyright 2006-2007  MenTaLguY <mental at rydia.net>
+ *
+ * RDoc taken from original.
+ *
+ * This file is made available under the same terms as Ruby.
+ */
+
+#include <ruby.h>
+#include <intern.h>
+#include <rubysig.h>
+
+static VALUE rb_cMutex;
+static VALUE rb_cConditionVariable;
+static VALUE rb_cQueue;
+static VALUE rb_cSizedQueue;
+/* earlier versions of ruby do not export rb_eThreadError */
+static VALUE private_eThreadError;
+
+static VALUE set_critical(VALUE value);
+
+/*
+ *  call-seq:
+ *     Thread.exclusive { block }   => obj
+ *  
+ *  Wraps a block in Thread.critical, restoring the original value
+ *  upon exit from the critical section, and returns the value of the
+ *  block.
+ */
+
+typedef struct _Entry {
+    VALUE value;
+    struct _Entry *next;
+} Entry;
+
+typedef struct _List {
+    Entry *entries;
+    Entry *last_entry;
+    Entry *entry_pool;
+    unsigned long size;
+} List;
+
+static void
+init_list(List *list)
+{
+    list->entries = NULL;
+    list->last_entry = NULL;
+    list->entry_pool = NULL;
+    list->size = 0;
+}
+
+static void
+mark_list(List *list)
+{
+    Entry *entry;
+    for (entry = list->entries; entry; entry = entry->next) {
+        rb_gc_mark(entry->value);
+    }
+}
+
+static void
+free_entries(Entry *first)
+{
+    Entry *next;
+    while (first) {
+        next = first->next;
+        xfree(first);
+        first = next;
+    }
+}
+
+static void
+finalize_list(List *list)
+{
+    free_entries(list->entries);
+    free_entries(list->entry_pool);
+}
+
+static void
+push_list(List *list, VALUE value)
+{
+    Entry *entry;
+
+    if (list->entry_pool) {
+        entry = list->entry_pool;
+        list->entry_pool = entry->next;
+    } else {
+        entry = ALLOC(Entry);
+    }
+
+    entry->value = value;
+    entry->next = NULL;
+
+    if (list->last_entry) {
+        list->last_entry->next = entry;
+    } else {
+        list->entries = entry;
+    }
+    list->last_entry = entry;
+
+    ++list->size;
+}
+
+static void
+push_multiple_list(List *list, VALUE *values, unsigned count)
+{
+    unsigned i;
+    for (i = 0; i < count; i++) {
+        push_list(list, values[i]);
+    }
+}
+
+static void
+recycle_entries(List *list, Entry *first_entry, Entry *last_entry)
+{
+#ifdef USE_MEM_POOLS
+    last_entry->next = list->entry_pool;
+    list->entry_pool = first_entry;
+#else
+    last_entry->next = NULL;
+    free_entries(first_entry);
+#endif
+}
+
+static VALUE
+shift_list(List *list)
+{
+    Entry *entry;
+    VALUE value;
+
+    entry = list->entries;
+    if (!entry) return Qundef;
+
+    list->entries = entry->next;
+    if (entry == list->last_entry) {
+        list->last_entry = NULL;
+    }
+
+    --list->size;
+
+    value = entry->value;
+    recycle_entries(list, entry, entry);
+
+    return value;
+}
+
+static void
+remove_one(List *list, VALUE value)
+{
+    Entry **ref;
+    Entry *prev;
+    Entry *entry;
+
+    for (ref = &list->entries, prev = NULL, entry = list->entries;
+              entry != NULL;
+              ref = &entry->next, prev = entry, entry = entry->next) {
+        if (entry->value == value) {
+            *ref = entry->next;
+            list->size--;
+            if (!entry->next) {
+                list->last_entry = prev;
+            }
+            recycle_entries(list, entry, entry);
+            break;
+        }
+    }
+}
+
+static void
+clear_list(List *list)
+{
+    if (list->last_entry) {
+        recycle_entries(list, list->entries, list->last_entry);
+        list->entries = NULL;
+        list->last_entry = NULL;
+        list->size = 0;
+    }
+}
+
+static VALUE
+array_from_list(List const *list)
+{
+    VALUE ary;
+    Entry *entry;
+    ary = rb_ary_new();
+    for (entry = list->entries; entry; entry = entry->next) {
+        rb_ary_push(ary, entry->value);
+    }
+    return ary;
+}
+
+static VALUE
+wake_thread(VALUE thread)
+{
+    return rb_rescue2(rb_thread_wakeup, thread,
+      NULL, Qundef, private_eThreadError, 0);
+}
+
+static VALUE
+run_thread(VALUE thread)
+{
+    return rb_rescue2(rb_thread_run, thread,
+      NULL, Qundef, private_eThreadError, 0);
+}
+
+static VALUE
+wake_one(List *list)
+{
+    VALUE waking;
+
+    waking = Qnil;
+    while (list->entries && !RTEST(waking)) {
+        waking = wake_thread(shift_list(list));
+    }
+
+    return waking;
+}
+
+static VALUE
+wake_all(List *list)
+{
+    while (list->entries) {
+        wake_one(list);
+    }
+    return Qnil;
+}
+
+static VALUE
+wait_list_inner(List *list)
+{
+    push_list(list, rb_thread_current());
+    rb_thread_stop();
+    return Qnil;
+}
+
+static VALUE
+wait_list_cleanup(List *list)
+{
+    /* cleanup in case of spurious wakeups */
+    remove_one(list, rb_thread_current());
+    return Qnil;
+}
+
+static void
+wait_list(List *list)
+{
+    rb_ensure(wait_list_inner, (VALUE)list, wait_list_cleanup, (VALUE)list);
+}
+
+static void
+assert_no_survivors(List *waiting, const char *label, void *addr)
+{
+    Entry *entry;
+    for (entry = waiting->entries; entry; entry = entry->next) {
+        if (RTEST(wake_thread(entry->value))) {
+            rb_bug("%s %p freed with live thread(s) waiting", label, addr);
+        }
+    }
+}
+
+/*
+ * Document-class: Mutex
+ *
+ * Mutex implements a simple semaphore that can be used to coordinate access to
+ * shared data from multiple concurrent threads.
+ *
+ * Example:
+ *
+ *   require 'thread'
+ *   semaphore = Mutex.new
+ *
+ *   a = Thread.new {
+ *     semaphore.synchronize {
+ *       # access shared resource
+ *     }
+ *   }
+ *
+ *   b = Thread.new {
+ *     semaphore.synchronize {
+ *       # access shared resource
+ *     }
+ *   }
+ *
+ */
+
+typedef struct _Mutex {
+    VALUE owner;
+    List waiting;
+} Mutex;
+
+static void
+mark_mutex(Mutex *mutex)
+{
+    rb_gc_mark(mutex->owner);
+    mark_list(&mutex->waiting);
+}
+
+static void
+finalize_mutex(Mutex *mutex)
+{
+    finalize_list(&mutex->waiting);
+}
+
+static void
+free_mutex(Mutex *mutex)
+{
+    assert_no_survivors(&mutex->waiting, "mutex", mutex);
+    finalize_mutex(mutex);
+    xfree(mutex);
+}
+
+static void
+init_mutex(Mutex *mutex)
+{
+    mutex->owner = Qnil;
+    init_list(&mutex->waiting);
+}
+
+/*
+ * Document-method: new
+ * call-seq: Mutex.new
+ * 
+ * Creates a new Mutex
+ *
+ */
+
+static VALUE 
+rb_mutex_alloc(VALUE klass)
+{
+    Mutex *mutex;
+    mutex = ALLOC(Mutex);
+    init_mutex(mutex);
+    return Data_Wrap_Struct(klass, mark_mutex, free_mutex, mutex);
+}
+
+/*
+ * Document-method: locked?
+ * call-seq: locked?
+ *
+ * Returns +true+ if this lock is currently held by some thread.
+ *
+ */
+
+static VALUE
+rb_mutex_locked_p(VALUE self)
+{
+    Mutex *mutex;
+    Data_Get_Struct(self, Mutex, mutex);
+    return RTEST(mutex->owner) ? Qtrue : Qfalse;
+}
+
+/*
+ * Document-method: try_lock
+ * call-seq: try_lock
+ *
+ * Attempts to obtain the lock and returns immediately. Returns +true+ if the
+ * lock was granted.
+ *
+ */
+
+static VALUE
+rb_mutex_try_lock(VALUE self)
+{
+    Mutex *mutex;
+
+    Data_Get_Struct(self, Mutex, mutex);
+
+    if (RTEST(mutex->owner))
+        return Qfalse;
+
+    mutex->owner = rb_thread_current();
+    return Qtrue;
+}
+
+/*
+ * Document-method: lock
+ * call-seq: lock
+ *
+ * Attempts to grab the lock and waits if it isn't available.
+ *
+ */
+
+static VALUE
+lock_mutex(Mutex *mutex)
+{
+    VALUE current;
+    current = rb_thread_current();
+
+    rb_thread_critical = 1;
+
+    while (RTEST(mutex->owner)) {
+        wait_list(&mutex->waiting);
+        rb_thread_critical = 1;
+    }
+    mutex->owner = current; 
+
+    rb_thread_critical = 0;
+    return Qnil;
+}
+
+static VALUE
+rb_mutex_lock(VALUE self)
+{
+    Mutex *mutex;
+    Data_Get_Struct(self, Mutex, mutex);
+    lock_mutex(mutex);
+    return self;
+}
+
+/*
+ * Document-method: unlock
+ *
+ * Releases the lock. Returns +nil+ if ref wasn't locked.
+ *
+ */
+
+static VALUE
+unlock_mutex_inner(Mutex *mutex)
+{
+    VALUE waking;
+
+    if (!RTEST(mutex->owner)) {
+	return Qundef;
+    }
+
+    mutex->owner = Qnil;
+    waking = wake_one(&mutex->waiting);
+
+    return waking;
+}
+
+static VALUE
+set_critical(VALUE value)
+{
+    rb_thread_critical = (int)value;
+    return Qundef;
+}
+
+static VALUE
+unlock_mutex(Mutex *mutex)
+{
+    VALUE waking;
+
+    rb_thread_critical = 1;
+    waking = rb_ensure(unlock_mutex_inner, (VALUE)mutex, set_critical, 0);
+
+    if (waking == Qundef) {
+        return Qfalse;
+    }
+
+    if (RTEST(waking)) {
+        run_thread(waking);
+    }
+
+    return Qtrue;
+}
+
+static VALUE
+rb_mutex_unlock(VALUE self)
+{
+    Mutex *mutex;
+    Data_Get_Struct(self, Mutex, mutex);
+
+    if (RTEST(unlock_mutex(mutex))) {
+        return self;
+    } else {
+        return Qnil;
+    }
+}
+
+/*
+ * Document-method: exclusive_unlock
+ * call-seq: exclusive_unlock { ... }
+ *
+ * If the mutex is locked, unlocks the mutex, wakes one waiting thread, and
+ * yields in a critical section.
+ *
+ */
+
+static VALUE
+rb_mutex_exclusive_unlock_inner(Mutex *mutex)
+{
+    VALUE waking;
+    waking = unlock_mutex_inner(mutex);
+    rb_yield(Qundef);
+    return waking;
+}
+
+static VALUE
+rb_mutex_exclusive_unlock(VALUE self)
+{
+    Mutex *mutex;
+    VALUE waking;
+    Data_Get_Struct(self, Mutex, mutex);
+
+    rb_thread_critical = 1;
+    waking = rb_ensure(rb_mutex_exclusive_unlock_inner, (VALUE)mutex, set_critical, 0);
+
+    if (waking == Qundef) {
+        return Qnil;
+    }
+
+    if (RTEST(waking)) {
+        run_thread(waking);
+    }
+
+    return self;
+}
+
+/*
+ * Document-method: synchronize
+ * call-seq: synchronize { ... }
+ *
+ * Obtains a lock, runs the block, and releases the lock when the block
+ * completes.  See the example under Mutex.
+ *
+ */
+
+static VALUE
+rb_mutex_synchronize(VALUE self)
+{
+    rb_mutex_lock(self);
+    return rb_ensure(rb_yield, Qundef, rb_mutex_unlock, self);
+}
+
+/*
+ * Document-class: ConditionVariable
+ *
+ * ConditionVariable objects augment class Mutex. Using condition variables,
+ * it is possible to suspend while in the middle of a critical section until a
+ * resource becomes available.
+ *
+ * Example:
+ *
+ *   require 'thread'
+ *
+ *   mutex = Mutex.new
+ *   resource = ConditionVariable.new
+ *
+ *   a = Thread.new {
+ *     mutex.synchronize {
+ *       # Thread 'a' now needs the resource
+ *       resource.wait(mutex)
+ *       # 'a' can now have the resource
+ *     }
+ *   }
+ *
+ *   b = Thread.new {
+ *     mutex.synchronize {
+ *       # Thread 'b' has finished using the resource
+ *       resource.signal
+ *     }
+ *   }
+ *
+ */
+
+typedef struct _ConditionVariable {
+    List waiting;
+} ConditionVariable;
+
+static void
+mark_condvar(ConditionVariable *condvar)
+{
+    mark_list(&condvar->waiting);
+}
+
+static void
+finalize_condvar(ConditionVariable *condvar)
+{
+    finalize_list(&condvar->waiting);
+}
+
+static void
+free_condvar(ConditionVariable *condvar)
+{
+    assert_no_survivors(&condvar->waiting, "condition variable", condvar);
+    finalize_condvar(condvar);
+    xfree(condvar);
+}
+
+static void
+init_condvar(ConditionVariable *condvar)
+{
+    init_list(&condvar->waiting);
+}
+
+/*
+ * Document-method: new
+ * call-seq: ConditionVariable.new
+ *
+ * Creates a new ConditionVariable
+ *
+ */
+
+static VALUE
+rb_condvar_alloc(VALUE klass)
+{
+    ConditionVariable *condvar;
+
+    condvar = ALLOC(ConditionVariable);
+    init_condvar(condvar);
+
+    return Data_Wrap_Struct(klass, mark_condvar, free_condvar, condvar);
+}
+
+/*
+ * Document-method: wait
+ * call-seq: wait
+ *
+ * Releases the lock held in +mutex+ and waits; reacquires the lock on wakeup.
+ *
+ */
+
+static void
+wait_condvar(ConditionVariable *condvar, Mutex *mutex)
+{
+    rb_thread_critical = 1;
+    if (rb_thread_current() != mutex->owner) {
+        rb_thread_critical = 0;
+        rb_raise(private_eThreadError, "not owner of the synchronization mutex");
+    }
+    unlock_mutex_inner(mutex);
+    rb_ensure(wait_list, (VALUE)&condvar->waiting, lock_mutex, (VALUE)mutex);
+}
+
+static VALUE
+legacy_exclusive_unlock(VALUE mutex)
+{
+    return rb_funcall(mutex, rb_intern("exclusive_unlock"), 0);
+}
+
+typedef struct {
+    ConditionVariable *condvar;
+    VALUE mutex;
+} legacy_wait_args;
+
+static VALUE
+legacy_wait(VALUE unused, legacy_wait_args *args)
+{
+    wait_list(&args->condvar->waiting);
+    rb_funcall(args->mutex, rb_intern("lock"), 0);
+    return Qnil;
+}
+
+static VALUE
+rb_condvar_wait(VALUE self, VALUE mutex_v)
+{
+    ConditionVariable *condvar;
+    Data_Get_Struct(self, ConditionVariable, condvar);
+
+    if (CLASS_OF(mutex_v) != rb_cMutex) {
+        /* interoperate with legacy mutex */
+        legacy_wait_args args;
+        args.condvar = condvar;
+        args.mutex = mutex_v;
+        rb_iterate(legacy_exclusive_unlock, mutex_v, legacy_wait, (VALUE)&args);
+    } else {
+        Mutex *mutex;
+        Data_Get_Struct(mutex_v, Mutex, mutex);
+        wait_condvar(condvar, mutex);
+    }
+
+    return self;
+}
+
+/*
+ * Document-method: broadcast
+ * call-seq: broadcast
+ *
+ * Wakes up all threads waiting for this condition.
+ *
+ */
+
+static VALUE
+rb_condvar_broadcast(VALUE self)
+{
+    ConditionVariable *condvar;
+
+    Data_Get_Struct(self, ConditionVariable, condvar);
+  
+    rb_thread_critical = 1;
+    rb_ensure(wake_all, (VALUE)&condvar->waiting, set_critical, 0);
+    rb_thread_schedule();
+
+    return self;
+}
+
+/*
+ * Document-method: signal
+ * call-seq: signal
+ *
+ * Wakes up the first thread in line waiting for this condition.
+ *
+ */
+
+static void
+signal_condvar(ConditionVariable *condvar)
+{
+    VALUE waking;
+    rb_thread_critical = 1;
+    waking = rb_ensure(wake_one, (VALUE)&condvar->waiting, set_critical, 0);
+    if (RTEST(waking)) {
+        run_thread(waking);
+    }
+}
+
+static VALUE
+rb_condvar_signal(VALUE self)
+{
+    ConditionVariable *condvar;
+    Data_Get_Struct(self, ConditionVariable, condvar);
+    signal_condvar(condvar);
+    return self;
+}
+
+/*
+ * Document-class: Queue
+ *
+ * This class provides a way to synchronize communication between threads.
+ *
+ * Example:
+ *
+ *   require 'thread'
+ *
+ *   queue = Queue.new
+ *
+ *   producer = Thread.new do
+ *     5.times do |i|
+ *       sleep rand(i) # simulate expense
+ *       queue << i
+ *       puts "#{i} produced"
+ *     end
+ *   end
+ *
+ *   consumer = Thread.new do
+ *     5.times do |i|
+ *       value = queue.pop
+ *       sleep rand(i/2) # simulate expense
+ *       puts "consumed #{value}"
+ *     end
+ *   end
+ *
+ *   consumer.join
+ *
+ */
+
+typedef struct _Queue {
+    Mutex mutex;
+    ConditionVariable value_available;
+    ConditionVariable space_available;
+    List values;
+    unsigned long capacity;
+} Queue;
+
+static void
+mark_queue(Queue *queue)
+{
+    mark_mutex(&queue->mutex);
+    mark_condvar(&queue->value_available);
+    mark_condvar(&queue->space_available);
+    mark_list(&queue->values);
+}
+
+static void
+finalize_queue(Queue *queue)
+{
+    finalize_mutex(&queue->mutex);
+    finalize_condvar(&queue->value_available);
+    finalize_condvar(&queue->space_available);
+    finalize_list(&queue->values);
+}
+
+static void
+free_queue(Queue *queue)
+{
+    assert_no_survivors(&queue->mutex.waiting, "queue", queue);
+    assert_no_survivors(&queue->space_available.waiting, "queue", queue);
+    assert_no_survivors(&queue->value_available.waiting, "queue", queue);
+    finalize_queue(queue);
+    xfree(queue);
+}
+
+static void
+init_queue(Queue *queue)
+{
+    init_mutex(&queue->mutex);
+    init_condvar(&queue->value_available);
+    init_condvar(&queue->space_available);
+    init_list(&queue->values);
+    queue->capacity = 0;
+}
+
+/*
+ * Document-method: new
+ * call-seq: new
+ *
+ * Creates a new queue.
+ *
+ */
+
+static VALUE
+rb_queue_alloc(VALUE klass)
+{
+    Queue *queue;
+    queue = ALLOC(Queue);
+    init_queue(queue);
+    return Data_Wrap_Struct(klass, mark_queue, free_queue, queue);
+}
+
+static VALUE
+rb_queue_marshal_load(VALUE self, VALUE data)
+{
+    Queue *queue;
+    VALUE array;
+    Data_Get_Struct(self, Queue, queue);
+
+    array = rb_marshal_load(data);
+    if (TYPE(array) != T_ARRAY) {
+        rb_raise(rb_eRuntimeError, "expected Array of queue data");
+    }
+    if (RARRAY(array)->len < 1) {
+        rb_raise(rb_eRuntimeError, "missing capacity value");
+    }
+    queue->capacity = NUM2ULONG(rb_ary_shift(array));
+    push_multiple_list(&queue->values, RARRAY(array)->ptr, (unsigned)RARRAY(array)->len);
+
+    return self;
+}
+
+static VALUE
+rb_queue_marshal_dump(VALUE self)
+{
+    Queue *queue;
+    VALUE array;
+    Data_Get_Struct(self, Queue, queue);
+
+    array = array_from_list(&queue->values);
+    rb_ary_unshift(array, ULONG2NUM(queue->capacity));
+    return rb_marshal_dump(array, Qnil);
+}
+
+/*
+ * Document-method: clear
+ * call-seq: clear
+ *
+ * Removes all objects from the queue.
+ *
+ */
+
+static VALUE
+rb_queue_clear(VALUE self)
+{
+    Queue *queue;
+    Data_Get_Struct(self, Queue, queue);
+
+    lock_mutex(&queue->mutex);
+    clear_list(&queue->values);
+    signal_condvar(&queue->space_available);
+    unlock_mutex(&queue->mutex);
+
+    return self;
+}
+
+/*
+ * Document-method: empty?
+ * call-seq: empty?
+ *
+ * Returns +true+ if the queue is empty.
+ *
+ */
+
+static VALUE
+rb_queue_empty_p(VALUE self)
+{
+    Queue *queue;
+    VALUE result;
+    Data_Get_Struct(self, Queue, queue);
+
+    lock_mutex(&queue->mutex);
+    result = queue->values.size == 0 ? Qtrue : Qfalse;
+    unlock_mutex(&queue->mutex);
+
+    return result;
+}
+
+/*
+ * Document-method: length
+ * call-seq: length
+ *
+ * Returns the length of the queue.
+ *
+ */
+
+static VALUE
+rb_queue_length(VALUE self)
+{
+    Queue *queue;
+    VALUE result;
+    Data_Get_Struct(self, Queue, queue);
+
+    lock_mutex(&queue->mutex);
+    result = ULONG2NUM(queue->values.size);
+    unlock_mutex(&queue->mutex);
+
+    return result;
+}
+
+/*
+ * Document-method: num_waiting
+ * call-seq: num_waiting
+ *
+ * Returns the number of threads waiting on the queue.
+ *
+ */
+
+static VALUE
+rb_queue_num_waiting(VALUE self)
+{
+    Queue *queue;
+    VALUE result;
+    Data_Get_Struct(self, Queue, queue);
+
+    lock_mutex(&queue->mutex);
+    result = ULONG2NUM(queue->value_available.waiting.size +
+      queue->space_available.waiting.size);
+    unlock_mutex(&queue->mutex);
+
+    return result;
+}
+
+/*
+ * Document-method: pop
+ * call_seq: pop(non_block=false)
+ *
+ * Retrieves data from the queue.  If the queue is empty, the calling thread is
+ * suspended until data is pushed onto the queue.  If +non_block+ is true, the
+ * thread isn't suspended, and an exception is raised.
+ *
+ */
+
+static VALUE
+rb_queue_pop(int argc, VALUE *argv, VALUE self)
+{
+    Queue *queue;
+    int should_block;
+    VALUE result;
+    Data_Get_Struct(self, Queue, queue);
+
+    if (argc == 0) {
+        should_block = 1;
+    } else if (argc == 1) {
+        should_block = !RTEST(argv[0]);
+    } else {
+        rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
+    }
+
+    lock_mutex(&queue->mutex);
+    if (!queue->values.entries && !should_block) {
+        unlock_mutex(&queue->mutex);
+        rb_raise(private_eThreadError, "queue empty");
+    }
+
+    while (!queue->values.entries) {
+        wait_condvar(&queue->value_available, &queue->mutex);
+    }
+
+    result = shift_list(&queue->values);
+    if (queue->capacity && queue->values.size < queue->capacity) {
+        signal_condvar(&queue->space_available);
+    }
+    unlock_mutex(&queue->mutex);
+
+    return result;
+}
+
+/*
+ * Document-method: push
+ * call-seq: push(obj)
+ *
+ * Pushes +obj+ to the queue.
+ *
+ */
+
+static VALUE
+rb_queue_push(VALUE self, VALUE value)
+{
+    Queue *queue;
+    Data_Get_Struct(self, Queue, queue);
+
+    lock_mutex(&queue->mutex);
+    while (queue->capacity && queue->values.size >= queue->capacity) {
+        wait_condvar(&queue->space_available, &queue->mutex);
+    }
+    push_list(&queue->values, value);
+    signal_condvar(&queue->value_available);
+    unlock_mutex(&queue->mutex);
+
+    return self;
+}
+
+/*
+ * Document-class: SizedQueue
+ *
+ * This class represents queues of specified size capacity.  The push operation
+ * may be blocked if the capacity is full.
+ *
+ * See Queue for an example of how a SizedQueue works.
+ *
+ */
+
+/*
+ * Document-method: new
+ * call-seq: new
+ *
+ * Creates a fixed-length queue with a maximum size of +max+.
+ *
+ */
+
+/*
+ * Document-method: max
+ * call-seq: max
+ *
+ * Returns the maximum size of the queue.
+ *
+ */
+
+static VALUE
+rb_sized_queue_max(VALUE self)
+{
+    Queue *queue;
+    VALUE result;
+    Data_Get_Struct(self, Queue, queue);
+
+    lock_mutex(&queue->mutex);
+    result = ULONG2NUM(queue->capacity);
+    unlock_mutex(&queue->mutex);
+
+    return result;
+}
+
+/*
+ * Document-method: max=
+ * call-seq: max=(size)
+ *
+ * Sets the maximum size of the queue.
+ *
+ */
+
+static VALUE
+rb_sized_queue_max_set(VALUE self, VALUE value)
+{
+    Queue *queue;
+    unsigned long new_capacity;
+    unsigned long difference;
+    Data_Get_Struct(self, Queue, queue);
+
+    new_capacity = NUM2ULONG(value);
+
+    if (new_capacity < 1) {
+        rb_raise(rb_eArgError, "value must be positive");
+    }
+
+    lock_mutex(&queue->mutex);
+    if (queue->capacity && new_capacity > queue->capacity) {
+        difference = new_capacity - queue->capacity;
+    } else {
+        difference = 0;
+    }
+    queue->capacity = new_capacity;
+    for (; difference > 0; --difference) {
+        signal_condvar(&queue->space_available);
+    }
+    unlock_mutex(&queue->mutex);
+
+    return self;
+}
+
+/*
+ * Document-method: push
+ * call-seq: push(obj)
+ *
+ * Pushes +obj+ to the queue.  If there is no space left in the queue, waits
+ * until space becomes available.
+ *
+ */
+
+/*
+ * Document-method: pop
+ * call-seq: pop(non_block=false)
+ *
+ * Retrieves data from the queue and runs a waiting thread, if any.
+ *
+ */
+
+/* for marshalling mutexes and condvars */
+
+static VALUE
+dummy_load(VALUE self, VALUE string)
+{
+    return Qnil;
+}
+
+static VALUE
+dummy_dump(VALUE self)
+{
+    return rb_str_new2("");
+}
+
+
+static VALUE
+setup_classes(VALUE unused)
+{
+    rb_mod_remove_const(rb_cObject, ID2SYM(rb_intern("Mutex")));
+    rb_cMutex = rb_define_class("Mutex", rb_cObject);
+    rb_define_alloc_func(rb_cMutex, rb_mutex_alloc);
+    rb_define_method(rb_cMutex, "marshal_load", dummy_load, 1);
+    rb_define_method(rb_cMutex, "marshal_dump", dummy_dump, 0);
+    rb_define_method(rb_cMutex, "locked?", rb_mutex_locked_p, 0);
+    rb_define_method(rb_cMutex, "try_lock", rb_mutex_try_lock, 0);
+    rb_define_method(rb_cMutex, "lock", rb_mutex_lock, 0);
+    rb_define_method(rb_cMutex, "unlock", rb_mutex_unlock, 0);
+    rb_define_method(rb_cMutex, "exclusive_unlock", rb_mutex_exclusive_unlock, 0);
+    rb_define_method(rb_cMutex, "synchronize", rb_mutex_synchronize, 0);
+
+    rb_mod_remove_const(rb_cObject, ID2SYM(rb_intern("ConditionVariable")));
+    rb_cConditionVariable = rb_define_class("ConditionVariable", rb_cObject);
+    rb_define_alloc_func(rb_cConditionVariable, rb_condvar_alloc);
+    rb_define_method(rb_cConditionVariable, "marshal_load", dummy_load, 1);
+    rb_define_method(rb_cConditionVariable, "marshal_dump", dummy_dump, 0);
+    rb_define_method(rb_cConditionVariable, "wait", rb_condvar_wait, 1);
+    rb_define_method(rb_cConditionVariable, "broadcast", rb_condvar_broadcast, 0);
+    rb_define_method(rb_cConditionVariable, "signal", rb_condvar_signal, 0);
+
+    rb_mod_remove_const(rb_cObject, ID2SYM(rb_intern("Queue")));
+    rb_cQueue = rb_define_class("Queue", rb_cObject);
+    rb_define_alloc_func(rb_cQueue, rb_queue_alloc);
+    rb_define_method(rb_cQueue, "marshal_load", rb_queue_marshal_load, 1);
+    rb_define_method(rb_cQueue, "marshal_dump", rb_queue_marshal_dump, 0);
+    rb_define_method(rb_cQueue, "clear", rb_queue_clear, 0);
+    rb_define_method(rb_cQueue, "empty?", rb_queue_empty_p, 0);
+    rb_define_method(rb_cQueue, "length", rb_queue_length, 0);
+    rb_define_method(rb_cQueue, "num_waiting", rb_queue_num_waiting, 0);
+    rb_define_method(rb_cQueue, "pop", rb_queue_pop, -1);
+    rb_define_method(rb_cQueue, "push", rb_queue_push, 1);
+    rb_alias(rb_cQueue, rb_intern("enq"), rb_intern("push"));
+    rb_alias(rb_cQueue, rb_intern("<<"), rb_intern("push"));
+    rb_alias(rb_cQueue, rb_intern("deq"), rb_intern("pop"));
+    rb_alias(rb_cQueue, rb_intern("shift"), rb_intern("pop"));
+    rb_alias(rb_cQueue, rb_intern("size"), rb_intern("length"));
+
+    rb_mod_remove_const(rb_cObject, ID2SYM(rb_intern("SizedQueue")));
+    rb_cSizedQueue = rb_define_class("SizedQueue", rb_cQueue);
+    rb_define_method(rb_cSizedQueue, "initialize", rb_sized_queue_max_set, 1);
+    rb_define_method(rb_cSizedQueue, "clear", rb_queue_clear, 0);
+    rb_define_method(rb_cSizedQueue, "empty?", rb_queue_empty_p, 0);
+    rb_define_method(rb_cSizedQueue, "length", rb_queue_length, 0);
+    rb_define_method(rb_cSizedQueue, "num_waiting", rb_queue_num_waiting, 0);
+    rb_define_method(rb_cSizedQueue, "pop", rb_queue_pop, -1);
+    rb_define_method(rb_cSizedQueue, "push", rb_queue_push, 1);
+    rb_define_method(rb_cSizedQueue, "max", rb_sized_queue_max, 0);
+    rb_define_method(rb_cSizedQueue, "max=", rb_sized_queue_max_set, 1);
+    rb_alias(rb_cSizedQueue, rb_intern("<<"), rb_intern("push"));
+    rb_alias(rb_cSizedQueue, rb_intern("deq"), rb_intern("pop"));
+    rb_alias(rb_cSizedQueue, rb_intern("shift"), rb_intern("pop"));
+
+    return Qnil;
+}
+
+void
+Init_fastthread()
+{
+    int saved_critical;
+
+    rb_require("thread");
+
+    private_eThreadError = rb_const_get(rb_cObject, rb_intern("ThreadError"));
+
+    /* ensure that classes get replaced atomically */
+    saved_critical = rb_thread_critical;
+    rb_thread_critical = 1;
+    rb_ensure(setup_classes, Qnil, set_critical, (VALUE)saved_critical);
+}
+




More information about the Pkg-ruby-extras-commits mailing list