[Zope-dev] 2nd of back-to-back communications with Zope hangi ng?

Brian Lloyd Brian@digicool.com
Tue, 17 Aug 1999 16:43:13 -0400


This message is in MIME format. Since your mail reader does not understand
this format, some or all of this message may not be legible.

------ =_NextPart_000_01BEE8F1.20374970
Content-Type: text/plain

Hi Marc.

I _think_ we've found a fix for this - I've attached a new
select_trigger.py file that should replace the one in the 
ZServer/medusa directory of your installation. If you get a
chance to drop this updated file in and restart your site
(so that the change can take effect) can you let me know
if this takes care of the problem? 

Thanks!


Brian Lloyd        brian@digicool.com
Software Engineer  540.371.6909              
Digital Creations  http://www.digicool.com 




> -----Original Message-----
> From: Marc.Conley@mapics.com [mailto:Marc.Conley@mapics.com]
> Sent: Monday, August 16, 1999 1:31 PM
> To: zope-dev@zope.org
> Cc: Marc.Conley@mapics.com
> Subject: [Zope-dev] 2nd of back-to-back communications with Zope
> hanging?
> 
> 
> I am using 2.0.0b1. I am interested in whether or not anyone 
> else is having
> a problem
> with two back-to-back communications with Zope hanging for a 
> period of time but ultimately
> working?
> 
> I am opening a URL on a Java client using UrlConnection which 
> is requesting that Zope run an external
> method. The purpose of the external method is to create  a 
> folder and add  a property to the folder.
> 
> After I get the result back from the above external method, I 
> immediately open another Url from
> the Java client which calls another external method which 
> requests the information that was added
> (the folder's property).
> 
> When I open these Url's back-to-back, I finally get the 
> response I am looking for after about 30 seconds. If I put a
> 1/2 second delay before opening the 2nd Url, it eliminates 
> the problem--I get the response immediately. But, this
> is a bandaid for some kind of problem.
> 
> Can anyone explain what I am experiencing here--have you had 
> similar problems? Or even better,
> is this something that has been fixed recently. Overall, 
> 2.0.0b1 is stable, etc. for what I am doing, so I
> was trying to wait until the final general release 2.0 comes 
> out to switch.
> 
> Thanks for any help/tips/explanations you may have!
> 
> 
> _______________________________________________
> Zope-Dev maillist  -  Zope-Dev@zope.org
> http://www.zope.org/mailman/listinfo/zope-dev
> 
> (To receive general Zope announcements, see:
> http://www.zope.org/mailman/listinfo/zope-announce
> 
> For non-developer, user-level issues, 
> zope@zope.org, http://www.zope.org/mailman/listinfo/zope )
> 


------ =_NextPart_000_01BEE8F1.20374970
Content-Type: application/octet-stream;
	name="select_trigger.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="select_trigger.py"

# -*- Mode: Python; tab-width: 4 -*-=0A=
=0A=
VERSION_STRING =3D "$Id: select_trigger.py,v 1.6 1999/08/17 20:29:20 =
brian Exp $"=0A=
=0A=
import asyncore=0A=
import asynchat=0A=
=0A=
import os=0A=
import socket=0A=
import string=0A=
import thread=0A=
=0A=
if os.name =3D=3D 'posix':=0A=
=0A=
	class trigger (asyncore.file_dispatcher):=0A=
=0A=
		"Wake up a call to select() running in the main thread"=0A=
=0A=
		# This is useful in a context where you are using Medusa's I/O=0A=
		# subsystem to deliver data, but the data is generated by another=0A=
		# thread.  Normally, if Medusa is in the middle of a call to=0A=
		# select(), new output data generated by another thread will have=0A=
		# to sit until the call to select() either times out or returns.=0A=
		# If the trigger is 'pulled' by another thread, it should =
immediately=0A=
		# generate a READ event on the trigger object, which will force =
the=0A=
		# select() invocation to return.=0A=
=0A=
		# A common use for this facility: letting Medusa manage I/O for a=0A=
		# large number of connections; but routing each request through a=0A=
		# thread chosen from a fixed-size thread pool.  When a thread is=0A=
		# acquired, a transaction is performed, but output data is=0A=
		# accumulated into buffers that will be emptied more efficiently=0A=
		# by Medusa. [picture a server that can process database queries=0A=
		# rapidly, but doesn't want to tie up threads waiting to send data=0A=
		# to low-bandwidth connections]=0A=
=0A=
		# The other major feature provided by this class is the ability to=0A=
		# move work back into the main thread: if you call pull_trigger()=0A=
		# with a thunk argument, when select() wakes up and receives the=0A=
		# event it will call your thunk from within that thread.  The main=0A=
		# purpose of this is to remove the need to wrap thread locks =
around=0A=
		# Medusa's data structures, which normally do not need them.  [To =
see=0A=
		# why this is true, imagine this scenario: A thread tries to push =
some=0A=
		# new data onto a channel's outgoing data queue at the same time =
that=0A=
		# the main thread is trying to remove some]=0A=
=0A=
		def __init__ (self):=0A=
			r, w =3D os.pipe()=0A=
			self.trigger =3D w=0A=
			asyncore.file_dispatcher.__init__ (self, r)=0A=
			self.lock =3D thread.allocate_lock()=0A=
			self.thunks =3D []=0A=
=0A=
		def __repr__ (self):=0A=
			return '<select-trigger (pipe) at %x>' % id(self)=0A=
=0A=
		def readable (self):=0A=
			return 1=0A=
=0A=
		def writable (self):=0A=
			return 0=0A=
=0A=
		def handle_connect (self):=0A=
			pass=0A=
=0A=
		def pull_trigger (self, thunk=3DNone):=0A=
			# print 'PULL_TRIGGER: ', len(self.thunks)=0A=
			if thunk:=0A=
				try:=0A=
					self.lock.acquire()=0A=
					self.thunks.append (thunk)=0A=
				finally:=0A=
					self.lock.release()=0A=
			os.write (self.trigger, 'x')=0A=
=0A=
		def handle_read (self):=0A=
			self.recv (8192)=0A=
			try:=0A=
				self.lock.acquire()=0A=
				for thunk in self.thunks:=0A=
					try:=0A=
						thunk()=0A=
					except:=0A=
						(file, fun, line), t, v, tbinfo =3D =
asyncore.compact_traceback()=0A=
						self.log_info(=0A=
							'exception in trigger thunk: (%s:%s %s)' % (t, v, tbinfo),=0A=
							'error')=0A=
				self.thunks =3D []=0A=
			finally:=0A=
				self.lock.release()=0A=
=0A=
else:=0A=
=0A=
	class trigger (asyncore.dispatcher):=0A=
=0A=
		address =3D ('127.9.9.9', 19999)=0A=
=0A=
		def __init__ (self):=0A=
			a =3D socket.socket (socket.AF_INET, socket.SOCK_STREAM)=0A=
			w =3D socket.socket (socket.AF_INET, socket.SOCK_STREAM)=0A=
=0A=
			# set TCP_NODELAY to true to avoid buffering=0A=
			w.setsockopt(socket.IPPROTO_TCP, 1, 1)=0A=
=0A=
			# tricky: get a pair of connected sockets=0A=
			a.bind (self.address)=0A=
			a.listen (1)=0A=
			w.setblocking (0)=0A=
			try:=0A=
				w.connect (self.address)=0A=
			except:=0A=
				pass=0A=
			r, addr =3D a.accept()=0A=
			a.close()=0A=
			w.setblocking (1)=0A=
			self.trigger =3D w=0A=
=0A=
			asyncore.dispatcher.__init__ (self, r)=0A=
			self.lock =3D thread.allocate_lock()=0A=
			self.thunks =3D []=0A=
			self._trigger_connected =3D 0=0A=
=0A=
		def __repr__ (self):=0A=
			return '<select-trigger (loopback) at %x>' % id(self)=0A=
=0A=
		def readable (self):=0A=
			return 1=0A=
=0A=
		def writable (self):=0A=
			return 0=0A=
=0A=
		def handle_connect (self):=0A=
			pass=0A=
=0A=
		def pull_trigger (self, thunk=3DNone):=0A=
			if thunk:=0A=
				try:=0A=
					self.lock.acquire()=0A=
					self.thunks.append (thunk)=0A=
				finally:=0A=
					self.lock.release()=0A=
			self.trigger.send ('x')=0A=
=0A=
		def handle_read (self):=0A=
			self.recv (8192)=0A=
			try:=0A=
				self.lock.acquire()=0A=
				for thunk in self.thunks:=0A=
					try:=0A=
						thunk()=0A=
					except:=0A=
						(file, fun, line), t, v, tbinfo =3D =
asyncore.compact_traceback()=0A=
						self.log_info(=0A=
							'exception in trigger thunk: (%s:%s %s)' % (t, v, tbinfo),=0A=
							'error')=0A=
				self.thunks =3D []=0A=
			finally:=0A=
				self.lock.release()=0A=
=0A=
=0A=
the_trigger =3D None=0A=
=0A=
class trigger_file:=0A=
=0A=
	"A 'triggered' file object"=0A=
=0A=
	buffer_size =3D 4096=0A=
=0A=
	def __init__ (self, parent):=0A=
		global the_trigger=0A=
		if the_trigger is None:=0A=
			the_trigger =3D trigger()=0A=
		self.parent =3D parent=0A=
		self.buffer =3D ''=0A=
		=0A=
	def write (self, data):=0A=
		self.buffer =3D self.buffer + data=0A=
		if len(self.buffer) > self.buffer_size:=0A=
			d, self.buffer =3D self.buffer, ''=0A=
			the_trigger.pull_trigger (=0A=
				lambda d=3Dd,p=3Dself.parent: p.push (d)=0A=
				)=0A=
=0A=
	def writeline (self, line):=0A=
		self.write (line+'\r\n')=0A=
		=0A=
	def writelines (self, lines):=0A=
		self.write (=0A=
			string.joinfields (=0A=
				lines,=0A=
				'\r\n'=0A=
				) + '\r\n'=0A=
			)=0A=
=0A=
	def flush (self):=0A=
		if self.buffer:=0A=
			d, self.buffer =3D self.buffer, ''=0A=
			the_trigger.pull_trigger (=0A=
				lambda p=3Dself.parent,d=3Dd: p.push (d)=0A=
				)=0A=
=0A=
	def softspace (self, *args):=0A=
		pass=0A=
=0A=
	def close (self):=0A=
		# in a derived class, you may want to call trigger_close() =
instead.=0A=
		self.flush()=0A=
		self.parent =3D None=0A=
=0A=
	def trigger_close (self):=0A=
		d, self.buffer =3D self.buffer, ''=0A=
		p, self.parent =3D self.parent, None=0A=
		the_trigger.pull_trigger (=0A=
			lambda p=3Dp,d=3Dd: (p.push(d), p.close_when_done())=0A=
			)=0A=
=0A=
if __name__ =3D=3D '__main__':=0A=
	=0A=
	import time=0A=
=0A=
	def thread_function (output_file, i, n):=0A=
		print 'entering thread_function'=0A=
		while n:=0A=
			time.sleep (5)=0A=
			output_file.write ('%2d.%2d %s\r\n' % (i, n, output_file))=0A=
			output_file.flush()=0A=
			n =3D n - 1=0A=
		output_file.close()=0A=
		print 'exiting thread_function'=0A=
=0A=
	class thread_parent (asynchat.async_chat):=0A=
		=0A=
		def __init__ (self, conn, addr):=0A=
			self.addr =3D addr=0A=
			asynchat.async_chat.__init__ (self, conn)=0A=
			self.set_terminator ('\r\n')=0A=
			self.buffer =3D ''=0A=
			self.count =3D 0=0A=
=0A=
		def collect_incoming_data (self, data):=0A=
			self.buffer =3D self.buffer + data=0A=
=0A=
		def found_terminator (self):=0A=
			data, self.buffer =3D self.buffer, ''=0A=
			if not data:=0A=
				asyncore.close_all()=0A=
				print "done"=0A=
				return=0A=
			n =3D string.atoi (string.split (data)[0])=0A=
			tf =3D trigger_file (self)=0A=
			self.count =3D self.count + 1=0A=
			thread.start_new_thread (thread_function, (tf, self.count, n))=0A=
=0A=
	class thread_server (asyncore.dispatcher):=0A=
=0A=
		def __init__ (self, family=3Dsocket.AF_INET, address=3D('', =
9003)):=0A=
			asyncore.dispatcher.__init__ (self)=0A=
			self.create_socket (family, socket.SOCK_STREAM)=0A=
			self.set_reuse_addr()=0A=
			self.bind (address)=0A=
			self.listen (5)=0A=
=0A=
		def handle_accept (self):=0A=
			conn, addr =3D self.accept()=0A=
			tp =3D thread_parent (conn, addr)=0A=
=0A=
	thread_server()=0A=
	#asyncore.loop(1.0, use_poll=3D1)=0A=
	try:=0A=
		asyncore.loop ()=0A=
	except:=0A=
		asyncore.close_all()=0A=

------ =_NextPart_000_01BEE8F1.20374970--