Commit 0af8720b authored by Jim Fulton's avatar Jim Fulton

It is no longer necessary to symbolically link cPickle or

ZServer. ZServer is no longer necessary at all.

Fixed a race condition in __call__.

If an exception raised on the server which could not be
unpickled on the client could cause the client connection to
fail.
parent a3d8a6da
...@@ -85,10 +85,10 @@ ...@@ -85,10 +85,10 @@
"""Simple rpc mechanisms """Simple rpc mechanisms
""" """
__version__ = "$Revision: 1.12 $"[11:-2] __version__ = "$Revision: 1.13 $"[11:-2]
from ZODB.cPickle import loads from cPickle import loads
from ZODB import cPickle import cPickle
from thread import allocate_lock from thread import allocate_lock
from smac import SizedMessageAsyncConnection from smac import SizedMessageAsyncConnection
import socket, string, struct, asyncore, sys, time, select import socket, string, struct, asyncore, sys, time, select
...@@ -102,6 +102,9 @@ pickler=cPickle.Pickler() ...@@ -102,6 +102,9 @@ pickler=cPickle.Pickler()
pickler.fast=1 # Don't use the memo pickler.fast=1 # Don't use the memo
dump=pickler.dump dump=pickler.dump
class UnUnPickleableError(Exception):
"Couldn't unpickle a remote exception"
class asyncRPC(SizedMessageAsyncConnection): class asyncRPC(SizedMessageAsyncConnection):
__map=0 __map=0
...@@ -119,6 +122,10 @@ class asyncRPC(SizedMessageAsyncConnection): ...@@ -119,6 +122,10 @@ class asyncRPC(SizedMessageAsyncConnection):
self.__r=None self.__r=None
l.acquire() l.acquire()
l=allocate_lock() # Response lock used to wait for call results
self.__call_la=l.acquire
self.__call_lr=l.release
def connect(self, tryonce=1, log_type='client'): def connect(self, tryonce=1, log_type='client'):
t=self._tmin t=self._tmin
connection = self._connection connection = self._connection
...@@ -152,7 +159,7 @@ class asyncRPC(SizedMessageAsyncConnection): ...@@ -152,7 +159,7 @@ class asyncRPC(SizedMessageAsyncConnection):
return 1 return 1
def finishConnect(self, s): def finishConnect(self, s):
SizedMessageAsyncConnection.__init__(self, s, s.getpeername(), {}) SizedMessageAsyncConnection.__init__(self, s, '', {})
# we are our own socket map! # we are our own socket map!
def keys(self): return (self._fileno,) def keys(self): return (self._fileno,)
...@@ -190,30 +197,37 @@ class asyncRPC(SizedMessageAsyncConnection): ...@@ -190,30 +197,37 @@ class asyncRPC(SizedMessageAsyncConnection):
def __call__(self, *args): def __call__(self, *args):
args=dump(args,1) self.__call_la()
self.message_output(args) try:
self._last_args=args=dump(args,1)
self.message_output(args)
if self.__map: self.__Wakeup() # You dumb bastard if self.__map: self.__Wakeup() # You dumb bastard
else: self.readLoop() else: self.readLoop()
while 1: while 1:
r=self._read() r=self._read()
c=r[:1] c=r[:1]
if c=='R': if c=='R':
if r=='RN.': return None # Common case! if r=='RN.': return None # Common case!
return loads(r[1:]) return loads(r[1:])
if c=='E': if c=='E':
r=loads(r[1:]) try: r=loads(r[1:])
if type(r) is TupleType: raise r[0], r[1] except:
raise r raise UnUnPickleableError(r[1:])
oob=self._outOfBand if type(r) is TupleType: raise r[0], r[1]
if oob is not None: raise r
r=r[1:] oob=self._outOfBand
if r=='N.': r=None # Common case! if oob is not None:
else: r=loads(r) r=r[1:]
oob(c, r) if r=='N.': r=None # Common case!
else: else: r=loads(r)
raise UnrecognizedResult, r oob(c, r)
else:
raise UnrecognizedResult, r
finally:
self._last_args=''
self.__call_lr()
def sendMessage(self, *args): def sendMessage(self, *args):
self.message_output(dump(args,1)) self.message_output(dump(args,1))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment