[Python-checkins] python/dist/src/Lib/bsddb __init__.py,1.10,1.11

greg at users.sourceforge.net greg at users.sourceforge.net
Sun Nov 2 04:10:18 EST 2003


Update of /cvsroot/python/python/dist/src/Lib/bsddb
In directory sc8-pr-cvs1:/tmp/cvs-serv30540/Lib/bsddb

Modified Files:
	__init__.py 
Log Message:
* Fix the singlethreaded deadlocks occurring in the simple bsddb interface.
* Add support for multiple iterator/generator objects at once on the simple
  bsddb _DBWithCursor interface.


Index: __init__.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/bsddb/__init__.py,v
retrieving revision 1.10
retrieving revision 1.11
diff -C2 -d -r1.10 -r1.11
*** __init__.py	27 Sep 2003 23:00:19 -0000	1.10
--- __init__.py	2 Nov 2003 09:10:16 -0000	1.11
***************
*** 71,88 ****
      def __iter__(self):
          try:
!             yield self.first()[0]
!             next = self.next
              while 1:
!                 yield next()[0]
          except _bsddb.DBNotFoundError:
              return
  
      def iteritems(self):
          try:
!             yield self.first()
!             next = self.next
              while 1:
!                 yield next()
          except _bsddb.DBNotFoundError:
              return
  """
--- 71,142 ----
      def __iter__(self):
          try:
!             cur = self.db.cursor()
!             self._iter_cursors[str(cur)] = cur
! 
!             # since we're only returning keys, we call the cursor
!             # methods with flags=0, dlen=0, dofs=0
!             curkey = cur.first(0,0,0)[0]
!             yield curkey
! 
!             next = cur.next
              while 1:
!                 try:
!                     curkey = next(0,0,0)[0]
!                     yield curkey
!                 except _bsddb.DBCursorClosedError:
!                     # our cursor object was closed since we last yielded
!                     # create a new one and attempt to reposition to the
!                     # right place
!                     cur = self.db.cursor()
!                     self._iter_cursors[str(cur)] = cur
!                     # FIXME-20031101-greg: race condition.  cursor could
!                     # be closed by another thread before this set call.
!                     try:
!                         cur.set(curkey,0,0,0)
!                     except _bsddb.DBCursorClosedError:
!                         # halt iteration on race condition...
!                         raise _bsddb.DBNotFoundError
!                     next = cur.next
          except _bsddb.DBNotFoundError:
+             try:
+                 del self._iter_cursors[str(cur)]
+             except KeyError:
+                 pass
              return
  
      def iteritems(self):
          try:
!             cur = self.db.cursor()
!             self._iter_cursors[str(cur)] = cur
! 
!             kv = cur.first()
!             curkey = kv[0]
!             yield kv
! 
!             next = cur.next
              while 1:
!                 try:
!                     kv = next()
!                     curkey = kv[0]
!                     yield kv
!                 except _bsddb.DBCursorClosedError:
!                     # our cursor object was closed since we last yielded
!                     # create a new one and attempt to reposition to the
!                     # right place
!                     cur = self.db.cursor()
!                     self._iter_cursors[str(cur)] = cur
!                     # FIXME-20031101-greg: race condition.  cursor could
!                     # be closed by another thread before this set call.
!                     try:
!                         cur.set(curkey,0,0,0)
!                     except _bsddb.DBCursorClosedError:
!                         # halt iteration on race condition...
!                         raise _bsddb.DBNotFoundError
!                     next = cur.next
          except _bsddb.DBNotFoundError:
+             try:
+                 del self._iter_cursors[str(cur)]
+             except KeyError:
+                 pass
              return
  """
***************
*** 98,110 ****
      def __init__(self, db):
          self.db = db
-         self.dbc = None
          self.db.set_get_returns_none(0)
  
      def __del__(self):
          self.close()
  
      def _checkCursor(self):
          if self.dbc is None:
              self.dbc = self.db.cursor()
  
      def _checkOpen(self):
--- 152,202 ----
      def __init__(self, db):
          self.db = db
          self.db.set_get_returns_none(0)
  
+         # FIXME-20031101-greg: I believe there is still the potential
+         # for deadlocks in a multithreaded environment if someone
+         # attempts to use the any of the cursor interfaces in one
+         # thread while doing a put or delete in another thread.  The
+         # reason is that _checkCursor and _closeCursors are not atomic
+         # operations.  Doing our own locking around self.dbc,
+         # self.saved_dbc_key and self._iter_cursors could prevent this.
+         # TODO: A test case demonstrating the problem needs to be written.
+ 
+         # self.dbc is a DBCursor object used to implement the
+         # first/next/previous/last/set_location methods.
+         self.dbc = None
+         self.saved_dbc_key = None
+ 
+         # a collection of all DBCursor objects currently allocated
+         # by the _iter_mixin interface.
+         self._iter_cursors = {}
+ 
+ 
      def __del__(self):
          self.close()
  
+     def _get_dbc(self):
+         return self.dbc
+ 
      def _checkCursor(self):
          if self.dbc is None:
              self.dbc = self.db.cursor()
+             if self.saved_dbc_key is not None:
+                 self.dbc.set(self.saved_dbc_key)
+                 self.saved_dbc_key = None
+ 
+     # This method is needed for all non-cursor DB calls to avoid
+     # BerkeleyDB deadlocks (due to being opened with DB_INIT_LOCK
+     # and DB_THREAD to be thread safe) when intermixing database
+     # operations that use the cursor internally with those that don't.
+     def _closeCursors(self, save=True):
+         if self.dbc:
+             c = self.dbc
+             self.dbc = None
+             if save:
+                 self.saved_dbc_key = c.current(0,0,0)[0]
+             c.close()
+             del c
+         map(lambda c: c.close(), self._iter_cursors.values())
  
      def _checkOpen(self):
***************
*** 125,135 ****
--- 217,230 ----
      def __setitem__(self, key, value):
          self._checkOpen()
+         self._closeCursors()
          self.db[key] = value
  
      def __delitem__(self, key):
          self._checkOpen()
+         self._closeCursors()
          del self.db[key]
  
      def close(self):
+         self._closeCursors(save=False)
          if self.dbc is not None:
              self.dbc.close()





More information about the Python-checkins mailing list