[Zope-Checkins] CVS: Zope/lib/python/ThreadLock - _ThreadLock.c:1.2 __init__.py:1.2 setup.py:1.2 tests.py:1.2

Jim Fulton cvs-admin at zope.org
Fri Nov 28 11:46:40 EST 2003


Update of /cvs-repository/Zope/lib/python/ThreadLock
In directory cvs.zope.org:/tmp/cvs-serv5465/lib/python/ThreadLock

Added Files:
	_ThreadLock.c __init__.py setup.py tests.py 
Log Message:
Reimplemented thread locks using new-style extension classes.


=== Zope/lib/python/ThreadLock/_ThreadLock.c 1.1 => 1.2 ===
--- /dev/null	Fri Nov 28 11:46:40 2003
+++ Zope/lib/python/ThreadLock/_ThreadLock.c	Fri Nov 28 11:46:39 2003
@@ -0,0 +1,299 @@
+/*****************************************************************************
+
+  Copyright (c) 1996-2003 Zope Corporation and Contributors.
+  All Rights Reserved.
+
+  This software is subject to the provisions of the Zope Public License,
+  Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+  WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+  FOR A PARTICULAR PURPOSE
+
+ ****************************************************************************/
+
+static char ThreadLock_module_documentation[] = 
+""
+"\n$Id$"
+;
+
+#include "Python.h"
+
+#ifdef WITH_THREAD
+
+#include "listobject.h"
+
+#ifdef PyList_SET_ITEM
+
+#include "pythread.h"
+#define get_thread_ident PyThread_get_thread_ident
+#define acquire_lock PyThread_acquire_lock
+#define release_lock PyThread_release_lock
+#define type_lock PyThread_type_lock
+#define free_lock PyThread_free_lock
+#define allocate_lock PyThread_allocate_lock
+
+#else
+
+#include "thread.h"
+
+#endif
+
+#endif
+
+static PyObject *ErrorObject;
+
+/* ----------------------------------------------------- */
+
+#define UNLESS(E) if(!(E))
+
+/* Declarations for objects of type ThreadLock */
+
+typedef struct {
+  PyObject_HEAD
+  int count;
+  long id;
+#ifdef WITH_THREAD
+  type_lock lock;
+#endif
+} ThreadLockObject;
+
+staticforward PyTypeObject ThreadLockType;
+
+static int
+cacquire(ThreadLockObject *self, int wait)
+{
+  int acquired = 1;
+#ifdef WITH_THREAD
+  long id = get_thread_ident();
+#else
+  long id = 1;
+#endif
+  if(self->count >= 0 && self->id==id)
+    {
+      /* Somebody has locked me.  It is either the current thread or
+         another thread. */
+      /* So this thread has it.  I can't have a race condition, because,
+	 if another thread had the lock, then the id would not be this
+	 one. */
+      self->count++;
+    }
+  else
+    {
+#ifdef WITH_THREAD
+      Py_BEGIN_ALLOW_THREADS
+      acquired = acquire_lock(self->lock, wait ? WAIT_LOCK : NOWAIT_LOCK);
+      Py_END_ALLOW_THREADS
+#endif
+      if (acquired)
+        {
+          self->count=0;
+          self->id=id;
+        }
+    }
+  return acquired;
+}
+
+static PyObject *
+acquire(ThreadLockObject *self, PyObject *args)
+{
+  int wait = -1, acquired;
+  if (! PyArg_ParseTuple(args, "|i", &wait)) return NULL;
+  acquired=cacquire(self, wait);
+  if(acquired < 0) return NULL;
+  if (wait >= 0) return PyInt_FromLong(acquired);
+  Py_INCREF(Py_None);
+  return Py_None;
+}
+
+static int
+crelease(ThreadLockObject *self)
+{
+#ifdef WITH_THREAD
+  long id = get_thread_ident();
+#else
+  long id = 1;
+#endif
+
+  if(self->count >= 0 && self->id==id)
+    {
+      /* Somebody has locked me.  It is either the current thread or
+         another thread. */
+      /* So this thread has it.  I can't have a race condition, because,
+	 if another thread had the lock, then the id would not be this
+	 one. */
+      self->count--;
+#ifdef WITH_THREAD
+      if(self->count < 0) release_lock(self->lock);
+#endif
+    }
+  else
+    {
+      PyErr_SetString(ErrorObject, "release unlocked lock");
+      return -1;
+    }
+  return 0;
+}
+
+static PyObject *
+release(ThreadLockObject *self, PyObject *args)
+{
+  if (! PyArg_ParseTuple(args, "")) return NULL;
+  if(crelease(self) < 0) return NULL;
+  Py_INCREF(Py_None);
+  return Py_None;
+}
+
+static PyObject *
+call_method(ThreadLockObject *self, PyObject *args)
+{
+  PyObject *f, *a=0, *k=0;
+
+  UNLESS(PyArg_ParseTuple(args,"OO|O",&f, &a, &k)) return NULL;
+  if(cacquire(self, -1) < 0) return NULL;
+  f=PyEval_CallObjectWithKeywords(f,a,k);
+  if(crelease(self) < 0)
+    {
+      Py_XDECREF(f);
+      f=NULL;
+    }
+  return f;
+}
+
+static struct PyMethodDef ThreadLock_methods[] = {
+  {"guarded_apply", (PyCFunction)call_method, 1,
+   "guarded_apply(FUNCTION, ARGS[, KEYWORDS]) -- Make a guarded function call\n"
+   "\n"
+   "Acquire the lock, call the function, and then release the lock.\n"
+  },
+  {"acquire", (PyCFunction)acquire, 1,
+   "acquire([wait]) -- Acquire a lock, taking the thread ID into account"
+  },
+  {"release", (PyCFunction)release, 1,
+   "release() -- Release a lock, taking the thread ID into account"
+  },
+  {NULL,		NULL}		/* sentinel */
+};
+
+static void
+ThreadLock_dealloc(ThreadLockObject *self)
+{
+#ifdef WITH_THREAD
+  free_lock(self->lock);
+#endif
+  PyObject_DEL(self);
+}
+
+static PyObject *
+ThreadLock_getattr(ThreadLockObject *self, PyObject *name)
+{
+  char *cname;
+
+  if((cname=PyString_AsString(name)))
+    {
+      if(*cname=='c' && strcmp(cname,"count")==0)
+	return PyInt_FromLong(self->count);
+      if(*cname=='i' && strcmp(cname,"id")==0)
+	return PyInt_FromLong(self->id);
+      return Py_FindMethod(ThreadLock_methods, (PyObject *)self, cname);
+    }
+  PyErr_SetObject(PyExc_AttributeError, name);
+  return NULL;
+}
+
+static PyTypeObject ThreadLockType = {
+  PyObject_HEAD_INIT(NULL)
+  0,				/*ob_size*/
+  "ThreadLock",			/*tp_name*/
+  sizeof(ThreadLockObject),	/*tp_basicsize*/
+  0,				/*tp_itemsize*/
+  /* methods */
+  (destructor)ThreadLock_dealloc,	/*tp_dealloc*/
+  (printfunc)0,	/*tp_print*/
+  (getattrfunc)0,		/*obsolete tp_getattr*/
+  (setattrfunc)0,		/*obsolete tp_setattr*/
+  (cmpfunc)0,	/*tp_compare*/
+  (reprfunc)0,		/*tp_repr*/
+  0,		/*tp_as_number*/
+  0,		/*tp_as_sequence*/
+  0,		/*tp_as_mapping*/
+  (hashfunc)0,		/*tp_hash*/
+  (ternaryfunc)0,	/*tp_call*/
+  (reprfunc)0,		/*tp_str*/
+  (getattrofunc)ThreadLock_getattr,			/*tp_getattro*/
+  0,			/*tp_setattro*/
+  
+  /* Space for future expansion */
+  0L,0L,
+  "Thread-based lock objects\n"
+  "\n"
+  "These lock objects may be allocated multiple times by the same\n"
+  "thread, but may only be allocated by one thread at a time.\n"
+  "This is useful for locking instances in possibly nested method calls\n"
+};
+
+static PyObject *
+newThreadLockObject(ThreadLockObject *self, PyObject *args)
+{
+	
+  UNLESS(PyArg_ParseTuple(args,"")) return NULL;
+  UNLESS(self = PyObject_NEW(ThreadLockObject, &ThreadLockType)) return NULL;
+  self->count=-1;
+#ifdef WITH_THREAD
+  self->lock = allocate_lock();
+  if (self->lock == NULL) {
+    PyObject_DEL(self);
+    self = NULL;
+    PyErr_SetString(ErrorObject, "can't allocate lock");
+  }
+#endif
+  return (PyObject*)self;
+}
+
+static PyObject *
+ident(PyObject *self, PyObject *args)
+{
+#ifdef WITH_THREAD
+  return PyInt_FromLong(get_thread_ident());
+#else
+  return PyInt_FromLong(0);
+#endif
+}
+
+static struct PyMethodDef Module_methods[] = {
+  { "allocate_lock", (PyCFunction)newThreadLockObject, 1,
+    "allocate_lock() -- Return a new lock object"
+  },
+  { "get_ident", (PyCFunction)ident, 1,
+    "get_ident() -- Get the id of the current thread"
+  },
+  {NULL, (PyCFunction)NULL, 0, NULL}		/* sentinel */
+};
+
+void
+init_ThreadLock(void)
+{
+  PyObject *m, *d;
+
+  m = Py_InitModule4("_ThreadLock", Module_methods,
+		     ThreadLock_module_documentation,
+		     (PyObject*)NULL,PYTHON_API_VERSION);
+
+  d = PyModule_GetDict(m);
+
+  ThreadLockType.ob_type=&PyType_Type;
+  PyDict_SetItemString(d,"ThreadLockType", (PyObject*)&ThreadLockType);
+
+  ErrorObject = PyString_FromString("ThreadLock.error");
+  PyDict_SetItemString(d, "error", ErrorObject);
+
+#ifdef WITH_THREAD
+  PyDict_SetItemString(d, "WITH_THREAD", PyInt_FromLong(1));
+#else
+  PyDict_SetItemString(d, "WITH_THREAD", Py_None);
+#endif  
+	
+  /* Check for errors */
+  if (PyErr_Occurred())
+    Py_FatalError("can't initialize module ThreadLock");
+}


=== Zope/lib/python/ThreadLock/__init__.py 1.1 => 1.2 ===
--- /dev/null	Fri Nov 28 11:46:40 2003
+++ Zope/lib/python/ThreadLock/__init__.py	Fri Nov 28 11:46:39 2003
@@ -0,0 +1 @@
+from _ThreadLock import *


=== Zope/lib/python/ThreadLock/setup.py 1.1 => 1.2 ===
--- /dev/null	Fri Nov 28 11:46:40 2003
+++ Zope/lib/python/ThreadLock/setup.py	Fri Nov 28 11:46:39 2003
@@ -0,0 +1,22 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+from distutils.core import setup, Extension
+setup(name="_ThreadLock", version="2.0",
+      ext_modules=[
+         Extension("_ThreadLock", ["_ThreadLock.c"],
+                   include_dirs = ['.', '../ExtensionClass'],
+                   depends = ['../ExtensionClass/ExtensionClass.h']),
+         ])
+


=== Zope/lib/python/ThreadLock/tests.py 1.1 => 1.2 ===
--- /dev/null	Fri Nov 28 11:46:40 2003
+++ Zope/lib/python/ThreadLock/tests.py	Fri Nov 28 11:46:39 2003
@@ -0,0 +1,67 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""ThreadLock tests
+
+>>> lock = ThreadLock.allocate_lock()
+
+>>> twoshouldstart = threading.Event()
+
+>>> n = 0
+>>> readbytwo = None
+
+>>> def one():
+...     global n
+...     lock.acquire()
+...     twoshouldstart.set()
+...     for i in range(10):
+...         time.sleep(.001)
+...         lock.acquire()
+...         n += 1
+... 
+...     for i in range(10):
+...         time.sleep(.001)
+...         lock.release()
+... 
+...     lock.release()
+
+>>> def two():
+...     global readbytwo
+...     twoshouldstart.wait()
+...     lock.acquire()
+...     readbytwo = n
+...     lock.release()
+
+>>> ttwo = threading.Thread(target=two)
+>>> ttwo.start()
+>>> time.sleep(0.001)
+>>> tone = threading.Thread(target=one)
+>>> tone.start()
+>>> tone.join()
+>>> ttwo.join()
+>>> readbytwo
+10
+
+$Id$
+"""
+
+import ThreadLock, threading, time
+import unittest
+from doctest import DocTestSuite
+
+def test_suite():
+    return unittest.TestSuite((
+        DocTestSuite(),
+        ))
+
+if __name__ == '__main__': unittest.main()




More information about the Zope-Checkins mailing list