work in progress. trying a different way of implementing async socket
This commit is contained in:
		@ -498,8 +498,21 @@ extend Apex
 | 
				
			|||||||
	method(#dual) doesNotUnderstand: message_name
 | 
						method(#dual) doesNotUnderstand: message_name
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		## TODO: implement this properly
 | 
							## TODO: implement this properly
 | 
				
			||||||
		| class_name |
 | 
							| class_name ctx |
 | 
				
			||||||
		class_name := if (self class == Class) { self name } else { self class name }.
 | 
							class_name := if (self class == Class) { self name } else { self class name }.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## TOOD: IMPROVE THIS EXPERIMENTAL BACKTRACE...
 | 
				
			||||||
 | 
					System logNl: '== BACKTRACE =='.
 | 
				
			||||||
 | 
					ctx := thisContext.
 | 
				
			||||||
 | 
					while (ctx notNil)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (ctx class == MethodContext) { System logNl: (' ' & ctx method owner name & '>>' & ctx method name) }.
 | 
				
			||||||
 | 
						## TODO: include blockcontext???
 | 
				
			||||||
 | 
						ctx := ctx sender.
 | 
				
			||||||
 | 
					}.
 | 
				
			||||||
 | 
					System logNl: '== END OF BACKTRACE =='.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		NoSuchMessageException signal: (message_name & ' not understood by ' & class_name).
 | 
							NoSuchMessageException signal: (message_name & ' not understood by ' & class_name).
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -230,22 +230,23 @@ class AsyncHandle(Object)
 | 
				
			|||||||
	## the handle must be the first field in this object to match
 | 
						## the handle must be the first field in this object to match
 | 
				
			||||||
	## the internal representation used by various modules. (e.g. sck)
 | 
						## the internal representation used by various modules. (e.g. sck)
 | 
				
			||||||
	var(#get) handle := -1.
 | 
						var(#get) handle := -1.
 | 
				
			||||||
 | 
						var outsem := nil.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var inwc := 0, outwc := 0. ## input watcher count and ouput watcher count
 | 
						##method initialize
 | 
				
			||||||
	var insem, outsem.
 | 
						##{
 | 
				
			||||||
	var(#get,#set) inputAction, outputAction.
 | 
						##	^super initialize
 | 
				
			||||||
	var(#get) inputReady := false, outputReady := false.
 | 
						##}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	method close
 | 
						method close
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (self.handle >= 0)
 | 
							if (self.handle >= 0)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			if (self.insem notNil) 
 | 
								###if (self.insem notNil) 
 | 
				
			||||||
			{
 | 
								###{
 | 
				
			||||||
				System unsignal: self.insem;
 | 
								###	System unsignal: self.insem;
 | 
				
			||||||
				       removeAsyncSemaphore: self.insem.
 | 
								###	       removeAsyncSemaphore: self.insem.
 | 
				
			||||||
				self.insem := nil.
 | 
								###	self.insem := nil.
 | 
				
			||||||
			}.
 | 
								###}.
 | 
				
			||||||
			if (self.outsem notNil)
 | 
								if (self.outsem notNil)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				System unsignal: self.outsem;
 | 
									System unsignal: self.outsem;
 | 
				
			||||||
@ -253,151 +254,34 @@ class AsyncHandle(Object)
 | 
				
			|||||||
				self.outsem := nil.
 | 
									self.outsem := nil.
 | 
				
			||||||
			}.
 | 
								}.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			self.outwc := 0.
 | 
					 | 
				
			||||||
			self.inwc := 0.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			self _close.
 | 
								self _close.
 | 
				
			||||||
			self.handle := -1.
 | 
								self.handle := -1.
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## TODO: how to specify a timeout for an action? using another semaphore??
 | 
					 | 
				
			||||||
	method watchInput
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if (self.inwc == 0) 
 | 
					 | 
				
			||||||
		{ 
 | 
					 | 
				
			||||||
			if (self.insem isNil)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				self.insem := Semaphore new.
 | 
					 | 
				
			||||||
				self.insem signalAction: [:sem | 
 | 
					 | 
				
			||||||
					self.inputReady := true.
 | 
					 | 
				
			||||||
					self.inputAction value: self value: true
 | 
					 | 
				
			||||||
				].
 | 
					 | 
				
			||||||
				System addAsyncSemaphore: self.insem.
 | 
					 | 
				
			||||||
			}.
 | 
					 | 
				
			||||||
			self.inputReady := false.
 | 
					 | 
				
			||||||
			System signal: self.insem onInput: self.handle 
 | 
					 | 
				
			||||||
		}.
 | 
					 | 
				
			||||||
		self.inwc := self.inwc + 1.
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	method unwatchInput
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if (self.inwc > 0)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			self.inwc := self.inwc - 1.
 | 
					 | 
				
			||||||
			if (self.inwc == 0)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				##if (self.insem notNil) { System unsignal: self.insem }.
 | 
					 | 
				
			||||||
				System unsignal: self.insem.
 | 
					 | 
				
			||||||
				System removeAsyncSemaphore: self.insem.
 | 
					 | 
				
			||||||
				self.insem := nil.
 | 
					 | 
				
			||||||
			}.
 | 
					 | 
				
			||||||
		}.
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	method watchOutput
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if (self.outwc == 0)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			if (self.outsem isNil)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				self.outsem := Semaphore new.
 | 
					 | 
				
			||||||
				self.outsem signalAction: [:sem | 
 | 
					 | 
				
			||||||
					self.outputReady := true.
 | 
					 | 
				
			||||||
					self.outputAction value: self value: true 
 | 
					 | 
				
			||||||
				].
 | 
					 | 
				
			||||||
				System addAsyncSemaphore: self.outsem.
 | 
					 | 
				
			||||||
			}.
 | 
					 | 
				
			||||||
			self.outputReady := false.
 | 
					 | 
				
			||||||
			System signal: self.outsem onOutput: self.handle.
 | 
					 | 
				
			||||||
		}.
 | 
					 | 
				
			||||||
		self.outwc := self.outwc + 1.
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	method unwatchOutput
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if (self.outwc > 0)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			self.outwc := self.outwc - 1.
 | 
					 | 
				
			||||||
			if (self.outwc == 0)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				## self.outsem must not be nil here.
 | 
					 | 
				
			||||||
				System unsignal: self.outsem.
 | 
					 | 
				
			||||||
				System removeAsyncSemaphore: self.outsem.
 | 
					 | 
				
			||||||
				self.outsem := nil.
 | 
					 | 
				
			||||||
			}.
 | 
					 | 
				
			||||||
		}.
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	method writeBytes: bytes offset: offset length: length signal: sem
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		| oldact n |
 | 
					 | 
				
			||||||
#######################################
 | 
					 | 
				
			||||||
## TODO: if data still in progress, failure... or success while concatening the message? 
 | 
					 | 
				
			||||||
##       for a stream, concatening is not bad. but it's not good if the socket requires message boundary preservation.
 | 
					 | 
				
			||||||
######################################
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (self.outputReady)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			## n >= 0: written
 | 
					 | 
				
			||||||
			## n <= -1: tolerable error (e.g. EAGAIN)
 | 
					 | 
				
			||||||
			## exception: fatal error
 | 
					 | 
				
			||||||
			##while (true) ## TODO: loop to write as much as possible
 | 
					 | 
				
			||||||
			##{
 | 
					 | 
				
			||||||
				n := self _writeBytes: bytes offset: offset length: length.
 | 
					 | 
				
			||||||
				if (n >= 0) 
 | 
					 | 
				
			||||||
				{ 
 | 
					 | 
				
			||||||
					if (sem notNil) { sem signal }.
 | 
					 | 
				
			||||||
					^n 
 | 
					 | 
				
			||||||
				}.
 | 
					 | 
				
			||||||
			##}.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			self.outputReady := false.
 | 
					 | 
				
			||||||
		}.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		oldact := self.outputAction.
 | 
					 | 
				
			||||||
		self.outputAction := [ :sck :state |
 | 
					 | 
				
			||||||
			##### schedule write.
 | 
					 | 
				
			||||||
			if (state)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				if ((self _writeBytes: bytes offset: offset length: length) <= -1) 
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					## EAGAIN
 | 
					 | 
				
			||||||
					self.outputReady := false.
 | 
					 | 
				
			||||||
					^self.
 | 
					 | 
				
			||||||
				}.
 | 
					 | 
				
			||||||
## TODO: handle _writeBytes may not write in full.
 | 
					 | 
				
			||||||
			}.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			self.outputAction := oldact.
 | 
					 | 
				
			||||||
			self unwatchOutput.
 | 
					 | 
				
			||||||
		].
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		## TODO: set timeout?
 | 
					 | 
				
			||||||
		self watchOutput.
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	method writeBytes: bytes signal: sem
 | 
						method writeBytes: bytes signal: sem
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		^self writeBytes: bytes offset: 0 length: (bytes size) signal: sem.
 | 
							^self writeBytes: bytes offset: 0 length: (bytes size)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	method writeBytes: bytes offset: offset length: length
 | 
						method writeBytes: bytes offset: offset length: length
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		^self writeBytes: bytes offset: offset length: length signal: nil.
 | 
							^self writeBytes: bytes offset: offset length: length.
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	method writeBytes: bytes
 | 
						method writeBytes: bytes
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		^self writeBytes: bytes offset: 0 length: (bytes size) signal: nil.
 | 
							^self writeBytes: bytes offset: 0 length: (bytes size)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Socket(AsyncHandle) from 'sck'
 | 
					class Socket(AsyncHandle) from 'sck'
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	method(#primitive) open(domain, type, proto).
 | 
						var eventActions.
 | 
				
			||||||
 | 
						var pending_bytes, pending_offset, pending_length.
 | 
				
			||||||
 | 
						var outreadysem, outdonesem, inreadysem.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						method(#primitive) _open(domain, type, proto).
 | 
				
			||||||
	method(#primitive) _close.
 | 
						method(#primitive) _close.
 | 
				
			||||||
	method(#primitive) bind: addr.
 | 
						method(#primitive) bind: addr.
 | 
				
			||||||
	method(#primitive) _listen: backlog.
 | 
						method(#primitive) _listen: backlog.
 | 
				
			||||||
@ -423,6 +307,13 @@ pooldic Socket.Type
 | 
				
			|||||||
	DGRAM  := 2.
 | 
						DGRAM  := 2.
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pooldic Socket.EventType
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						CONNECTED := 0.
 | 
				
			||||||
 | 
						DATA_IN := 1.
 | 
				
			||||||
 | 
						DATA_OUT := 2.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extend Socket
 | 
					extend Socket
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	method(#class) new { self messageProhibited: #new }
 | 
						method(#class) new { self messageProhibited: #new }
 | 
				
			||||||
@ -430,266 +321,191 @@ extend Socket
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	method(#class) domain: domain type: type
 | 
						method(#class) domain: domain type: type
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		^super new open(domain, type, 0).
 | 
							^(super new) open(domain, type, 0)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	method listen: backlog do: acceptBlock
 | 
						method initialize
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		self.inputAction := acceptBlock.
 | 
							super initialize.
 | 
				
			||||||
		self watchInput.
 | 
							self.eventActions := #(nil nil nil).
 | 
				
			||||||
		^self _listen: backlog.
 | 
					
 | 
				
			||||||
 | 
							self.outdonesem := Semaphore new.
 | 
				
			||||||
 | 
							self.outreadysem := Semaphore new.
 | 
				
			||||||
 | 
							self.inreadysem := Semaphore new.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							self.outdonesem signalAction: [ :xsem |
 | 
				
			||||||
 | 
								(self.eventActions at: Socket.EventType.DATA_OUT) value: self.
 | 
				
			||||||
 | 
								System unsignal: self.outreadysem.
 | 
				
			||||||
 | 
							].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							self.outreadysem signalAction: [ :xsem |
 | 
				
			||||||
 | 
								| nwritten |
 | 
				
			||||||
 | 
								nwritten := self _writeBytes: self.pending_bytes offset: self.pending_offset length: self.pending_length.
 | 
				
			||||||
 | 
								if (nwritten >= 0)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									self.pending_bytes := nil.
 | 
				
			||||||
 | 
									self.pending_offset := 0.
 | 
				
			||||||
 | 
									self.pending_length := 0.
 | 
				
			||||||
 | 
									self.outdonesem signal.
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							self.inreadysem signalAction: [ :ysem |
 | 
				
			||||||
 | 
								(self.eventActions at: Socket.EventType.DATA_IN) value: self.
 | 
				
			||||||
 | 
							].
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	method connect: target do: connectBlock
 | 
						method open(domain, type, proto)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		| conblk oldact |
 | 
							| sck |
 | 
				
			||||||
 | 
							sck := self _open(domain, type, proto).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (self.handle >= 0)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								System addAsyncSemaphore: self.outdonesem.
 | 
				
			||||||
 | 
								System addAsyncSemaphore: self.outreadysem.
 | 
				
			||||||
 | 
							}.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							^sck
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						method close
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
					'Socket close' dump.
 | 
				
			||||||
 | 
							System removeAsyncSemaphore: self.outdonesem.
 | 
				
			||||||
 | 
							System removeAsyncSemaphore: self.outreadysem.
 | 
				
			||||||
 | 
							^super close.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					###	method listen: backlog do: acceptBlock
 | 
				
			||||||
 | 
					###	{
 | 
				
			||||||
 | 
					###		self.inputAction := acceptBlock.
 | 
				
			||||||
 | 
					###		self watchInput.
 | 
				
			||||||
 | 
					###		^self _listen: backlog.
 | 
				
			||||||
 | 
					###	}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						method onEvent: event_type do: action_block
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							self.eventActions at: event_type put: action_block.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						method connect: target
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							| sem |
 | 
				
			||||||
		if ((self _connect: target) <= -1)
 | 
							if ((self _connect: target) <= -1)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			## connection in progress
 | 
								sem := Semaphore new.
 | 
				
			||||||
 | 
								sem signalAction: [ :xsem |
 | 
				
			||||||
			oldact := self.outputAction.
 | 
									| soerr dra |
 | 
				
			||||||
			self.outputAction := [ :sck :state |
 | 
					 | 
				
			||||||
				| soerr |
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if (state)
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					## i don't map a connection error to an exception.
 | 
					 | 
				
			||||||
					## it's not a typical exception. it is a normal failure
 | 
					 | 
				
			||||||
					## that is caused by an external system. 
 | 
					 | 
				
			||||||
					##
 | 
					 | 
				
			||||||
					## or should i map an error to an exception?
 | 
					 | 
				
			||||||
					## i can treat EINPROGRESS, ECONNREFUSED as failure.
 | 
					 | 
				
			||||||
					## all other errors may get treated as an exception?
 | 
					 | 
				
			||||||
					## what about timeout???
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				soerr := self _socketError.
 | 
									soerr := self _socketError.
 | 
				
			||||||
				if (soerr >= 0) 
 | 
									if (soerr >= 0) 
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					## finalize connection if not in progress
 | 
										## finalize connection if not in progress
 | 
				
			||||||
						self.outputAction := oldact.
 | 
					'CHECKING FOR CONNECTION.....' dump.
 | 
				
			||||||
						self unwatchOutput.
 | 
										System unsignal: xsem.
 | 
				
			||||||
						if (connectBlock notNil)
 | 
										System removeAsyncSemaphore: xsem.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										(self.eventActions at: Socket.EventType.CONNECTED) value: self value: (soerr == 0).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if (soerr == 0)
 | 
				
			||||||
					{
 | 
										{
 | 
				
			||||||
							connectBlock value: sck value: (soerr == 0).
 | 
											if ((self.eventActions at: Socket.EventType.DATA_IN) notNil)
 | 
				
			||||||
						}.
 | 
					 | 
				
			||||||
					}.
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				else
 | 
					 | 
				
			||||||
						{
 | 
											{
 | 
				
			||||||
					## timed out
 | 
												xsem signalAction: [ :ysem |
 | 
				
			||||||
					self.outputAction := oldact.
 | 
													(self.eventActions at: Socket.EventType.DATA_IN) value: self.
 | 
				
			||||||
					self unwatchOutput.
 | 
												].
 | 
				
			||||||
					if (connectBlock notNil) 
 | 
												System addAsyncSemaphore: xsem.
 | 
				
			||||||
					{
 | 
												System signal: xsem onInput: self.handle.
 | 
				
			||||||
						## TODO: tri-state? success, failure, timeout? or boolean with extra error code
 | 
					 | 
				
			||||||
						connectBlock value: sck value: false. 
 | 
					 | 
				
			||||||
						}.
 | 
											}.
 | 
				
			||||||
					}.
 | 
										}.
 | 
				
			||||||
 | 
									}.
 | 
				
			||||||
 | 
									(* HOW TO HANDLE TIMEOUT? *)
 | 
				
			||||||
			].
 | 
								].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			###self.outputTimeout: 10 do: xxxx.
 | 
								System addAsyncSemaphore: sem.
 | 
				
			||||||
			self watchOutput.
 | 
								System signal: sem onOutput: self.handle.
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			## connected immediately.
 | 
								## connected immediately
 | 
				
			||||||
			if (connectBlock notNil) 
 | 
					'IMMEDIATELY CONNECTED.....' dump.
 | 
				
			||||||
 | 
								(self.eventActions at: Socket.EventType.CONNECTED) value: self value: true.
 | 
				
			||||||
 | 
								if ((self.eventActions at: Socket.EventType.DATA_IN) notNil)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				connectBlock value: self value: true.
 | 
									sem := Semaphore new.
 | 
				
			||||||
 | 
									sem signalAction: [ :xsem |
 | 
				
			||||||
 | 
										(self.eventActions at: Socket.EventType.DATA_IN) value: self.
 | 
				
			||||||
 | 
									].
 | 
				
			||||||
 | 
									System addAsyncSemaphore: sem.
 | 
				
			||||||
 | 
									System signal: sem onInput: self.handle.
 | 
				
			||||||
 | 
								}.
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						method writeBytes: bytes offset: offset length: length
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							| n |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							## n >= 0: written
 | 
				
			||||||
 | 
							## n <= -1: tolerable error (e.g. EAGAIN)
 | 
				
			||||||
 | 
							## exception: fatal error
 | 
				
			||||||
 | 
							##while (true) ## TODO: loop to write as much as possible
 | 
				
			||||||
 | 
							##{
 | 
				
			||||||
 | 
								n := self _writeBytes: bytes offset: offset length: length.
 | 
				
			||||||
 | 
								if (n >= 0) 
 | 
				
			||||||
 | 
								{ 
 | 
				
			||||||
 | 
									self.outdonesem signal.
 | 
				
			||||||
 | 
									^n 
 | 
				
			||||||
 | 
								}.
 | 
				
			||||||
 | 
							##}.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							## TODO: adjust offset and length 
 | 
				
			||||||
 | 
							self.pending_bytes := bytes.
 | 
				
			||||||
 | 
							self.pending_offset := offset.
 | 
				
			||||||
 | 
							self.pending_length := length.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							System signal: self.outreadysem onOutput: self.handle.
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MyObject(Object)
 | 
					class MyObject(Object)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	method(#class) main
 | 
						method(#class) main
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		| conact inact outact accact |
 | 
							[
 | 
				
			||||||
 | 
								| s s2 st sg ss buf count |
 | 
				
			||||||
 | 
					 | 
				
			||||||
(SocketAddress fromString: '192.168.123.232:99') dump.
 | 
					 | 
				
			||||||
'****************************' dump.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
(*
 | 
					 | 
				
			||||||
s:= X new: 20.
 | 
					 | 
				
			||||||
s basicSize dump.
 | 
					 | 
				
			||||||
'****************************' dump.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
s := Y new: 10.
 | 
					 | 
				
			||||||
s x.
 | 
					 | 
				
			||||||
s basicAt: 1 put: 20.
 | 
					 | 
				
			||||||
s dump.
 | 
					 | 
				
			||||||
s basicSize dump.
 | 
					 | 
				
			||||||
'****************************' dump.
 | 
					 | 
				
			||||||
*)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
(***********************************
 | 
					 | 
				
			||||||
s := ByteArray new: 100.
 | 
					 | 
				
			||||||
s basicFillFrom: 0 with: ($a asInteger) count: 100.
 | 
					 | 
				
			||||||
s basicFillFrom: 50 with: ($b asInteger) count: 50.
 | 
					 | 
				
			||||||
(s basicShiftFrom: 50 to: 94 count: 10) dump.
 | 
					 | 
				
			||||||
s dump.
 | 
					 | 
				
			||||||
##thisProcess terminate.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
s := IP4Address fromString: '192.168.123.232'.
 | 
					 | 
				
			||||||
s dump.
 | 
					 | 
				
			||||||
s basicSize dump.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
s := IP6Address fromString: 'fe80::c225:e9ff:fe47:99.2.3.4'.
 | 
					 | 
				
			||||||
##s := IP6Address fromString: '::99.12.34.54'.
 | 
					 | 
				
			||||||
##s := IP6Address fromString: '::FFFF:0:0'.
 | 
					 | 
				
			||||||
##s := IP6Address fromString: 'fe80::'.
 | 
					 | 
				
			||||||
s dump.
 | 
					 | 
				
			||||||
s basicSize dump.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
s := IP6Address fromString: 'fe80::c225:e9ff:fe47:b1b6'.
 | 
					 | 
				
			||||||
s dump.
 | 
					 | 
				
			||||||
s basicSize dump.
 | 
					 | 
				
			||||||
##s := IP6Address new.
 | 
					 | 
				
			||||||
##s dump.
 | 
					 | 
				
			||||||
##s := IP4SocketAddress new.
 | 
					 | 
				
			||||||
##s dump.
 | 
					 | 
				
			||||||
thisProcess terminate.
 | 
					 | 
				
			||||||
**************************)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		inact := [:sck :state |
 | 
					 | 
				
			||||||
			| data n |
 | 
					 | 
				
			||||||
(*
 | 
					 | 
				
			||||||
end of data -> 0.
 | 
					 | 
				
			||||||
no data -> -1. (e.g. EAGAIN)
 | 
					 | 
				
			||||||
has data -> 1 or more
 | 
					 | 
				
			||||||
error -> exception
 | 
					 | 
				
			||||||
*)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			data := ByteArray new: 100.
 | 
					 | 
				
			||||||
			do
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				n := sck readBytes: data.
 | 
					 | 
				
			||||||
				if (n <= 0)
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					if (n == 0) { sck close }. ## end of data
 | 
					 | 
				
			||||||
					break.
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				elsif (n > 0)
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					(n asString & ' bytes read') dump.
 | 
					 | 
				
			||||||
					data dump.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					##sck writeBytes: #[ $h, $e, $l, $l, $o, $., $., $., C'\n' ].
 | 
					 | 
				
			||||||
					sck writeBytes: data offset: 0 length: n.
 | 
					 | 
				
			||||||
				}.
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			while (true).
 | 
					 | 
				
			||||||
		].
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## TODO: what should it accept as block parameter
 | 
					 | 
				
			||||||
## socket, output result? , output object?
 | 
					 | 
				
			||||||
		outact := [:sck :state |
 | 
					 | 
				
			||||||
			if (state)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				## what if i want write more data???
 | 
					 | 
				
			||||||
				##[ sck writeBytes: #[ $h, $e, $l, $l, $o, C'\n' ] ] 
 | 
					 | 
				
			||||||
				##	on: Exception do: [:ex | sck close. ].
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			else
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		].
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
		conact := [:sck :state |
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			| x write_more count |
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			count := 0.
 | 
								count := 0.
 | 
				
			||||||
 | 
								[
 | 
				
			||||||
 | 
									buf := ByteArray new: 128.
 | 
				
			||||||
 | 
									s := Socket domain: Socket.Domain.INET type: Socket.Type.STREAM.
 | 
				
			||||||
 | 
									
 | 
				
			||||||
 | 
									s onEvent: Socket.EventType.CONNECTED do: [ :sck :state |
 | 
				
			||||||
					if (state)
 | 
										if (state)
 | 
				
			||||||
					{
 | 
										{
 | 
				
			||||||
				'CONNECTED NOW.............' dump.
 | 
											'AAAAAAAA' dump.
 | 
				
			||||||
				###sck inputTimeout: 10; outputTimeout: 10; connectTimeout: 10.
 | 
											s writeBytes: #[ $a, $b, $c ] 
 | 
				
			||||||
 | 
					 | 
				
			||||||
#############################################
 | 
					 | 
				
			||||||
				write_more := [:sem |
 | 
					 | 
				
			||||||
					if (count <= 26)
 | 
					 | 
				
			||||||
					{
 | 
					 | 
				
			||||||
						sck writeBytes: %[ $h, $e, $l, $l, $o, $-, $m, $o, count + 65, $o, $o, C'\n' ] signal: x.
 | 
					 | 
				
			||||||
						count := count + 1.
 | 
					 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					else
 | 
										else
 | 
				
			||||||
					{
 | 
										{
 | 
				
			||||||
						System removeAsyncSemaphore: x.
 | 
											'FAILED TO CONNECT' dump.
 | 
				
			||||||
					}.
 | 
										}.
 | 
				
			||||||
				].
 | 
									].
 | 
				
			||||||
 | 
									s onEvent: Socket.EventType.DATA_IN do: [ :sck |
 | 
				
			||||||
				x := Semaphore new.
 | 
										| nbytes |
 | 
				
			||||||
				x signalAction: write_more.
 | 
										nbytes := s readBytes: buf. 
 | 
				
			||||||
				System addAsyncSemaphore: x.
 | 
										if (nbytes == 0)
 | 
				
			||||||
				x signal.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				##sck outputAction: outact.
 | 
					 | 
				
			||||||
				##sck writeBytes: #[ $h, $e, $l, $l, $o, $-, $m, $o, $o, C'\n' ] signal: x.
 | 
					 | 
				
			||||||
###############################################
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				sck inputAction: inact.
 | 
					 | 
				
			||||||
				sck watchInput.
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			else
 | 
					 | 
				
			||||||
					{
 | 
										{
 | 
				
			||||||
				'UNABLE TO CONNECT............' dump.
 | 
											sck close
 | 
				
			||||||
			}
 | 
										}.
 | 
				
			||||||
 | 
										('Got ' & (nbytes asString)) dump.
 | 
				
			||||||
 | 
										buf dump.
 | 
				
			||||||
 | 
									].
 | 
				
			||||||
 | 
									s onEvent: Socket.EventType.DATA_OUT do: [ :sck |
 | 
				
			||||||
 | 
										if (count < 10) { s writeBytes: #[ $a, $b, C'\n' ]. count := count + 1. }.
 | 
				
			||||||
				].
 | 
									].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		## ------------------------------------------------------
 | 
									s connect: (SocketAddress fromString: '127.0.0.1:9999').
 | 
				
			||||||
		accact := [:sck :state |
 | 
					 | 
				
			||||||
			| newsck newaddr |
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			newaddr := SocketAddress new.
 | 
					 | 
				
			||||||
			newsck := sck accept: newaddr.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			System log: 'new connection - '; log: newaddr; log: ' '; log: (newsck handle); logNl.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			newsck inputAction: inact; outputAction: outact.
 | 
					 | 
				
			||||||
			##newsck watchInput; watchOutput.
 | 
					 | 
				
			||||||
			newsck watchInput.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			newsck writeBytes: #[ $W, $e, $l, $c, $o, $m, $e, $., C'\n' ].
 | 
					 | 
				
			||||||
		].
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		[
 | 
					 | 
				
			||||||
			| s s2 st sg ss |
 | 
					 | 
				
			||||||
			[
 | 
					 | 
				
			||||||
				s := Socket domain: Socket.Domain.INET type: Socket.Type.STREAM.
 | 
					 | 
				
			||||||
				s connect: (SocketAddress fromString: '127.0.0.1:9999') do: conact.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
##				s2 := Socket domain: Socket.Domain.INET type: Socket.Type.STREAM.
 | 
					 | 
				
			||||||
##				s2 bind: (SocketAddress fromString: '0.0.0.0:9998').
 | 
					 | 
				
			||||||
##				##s2 inputAction: accact.
 | 
					 | 
				
			||||||
##				###s2 listen: 10; watchInput.
 | 
					 | 
				
			||||||
##				s2 listen: 10 do: accact.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
(*
 | 
					 | 
				
			||||||
st := Semaphore new.
 | 
					 | 
				
			||||||
System addAsyncSemaphore: st.
 | 
					 | 
				
			||||||
System signal: st afterSecs: 5.
 | 
					 | 
				
			||||||
'JJJJJJJJJJJ' dump.
 | 
					 | 
				
			||||||
sg := SemaphoreGroup new.
 | 
					 | 
				
			||||||
'JJJJJJJJJJJ' dump.
 | 
					 | 
				
			||||||
sg wait.
 | 
					 | 
				
			||||||
'YYYYYYYYYYYYYYY' dump.
 | 
					 | 
				
			||||||
*)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
###[ while (1) { '1111' dump. System sleepForSecs: 1 } ] fork.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
(*
 | 
					 | 
				
			||||||
st := Semaphore new.
 | 
					 | 
				
			||||||
System addAsyncSemaphore: st.
 | 
					 | 
				
			||||||
System signal: st afterSecs: 20.
 | 
					 | 
				
			||||||
*)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				while (true)
 | 
									while (true)
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
 | 
				
			|||||||
@ -373,7 +373,7 @@ moo_pfrc_t moo_pf_basic_at_put (moo_t* moo, moo_ooi_t nargs)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (MOO_OBJ_GET_FLAGS_RDONLY(rcv))
 | 
						if (MOO_OBJ_GET_FLAGS_RDONLY(rcv))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		moo_seterrbfmt (moo, MOO_EPERM, "now allowed to change a read-only object - %O", rcv);
 | 
							moo_seterrbfmt (moo, MOO_EPERM, "not allowed to change a read-only object - %O", rcv);
 | 
				
			||||||
		return MOO_PF_FAILURE;
 | 
							return MOO_PF_FAILURE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -471,7 +471,7 @@ moo_pfrc_t moo_pf_basic_fill (moo_t* moo, moo_ooi_t nargs)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (MOO_OBJ_GET_FLAGS_RDONLY(rcv))
 | 
						if (MOO_OBJ_GET_FLAGS_RDONLY(rcv))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		moo_seterrbfmt (moo, MOO_EPERM, "now allowed to change a read-only object - %O", rcv);
 | 
							moo_seterrbfmt (moo, MOO_EPERM, "not allowed to change a read-only object - %O", rcv);
 | 
				
			||||||
		return MOO_PF_FAILURE;
 | 
							return MOO_PF_FAILURE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -588,7 +588,7 @@ moo_pfrc_t moo_pf_basic_shift (moo_t* moo, moo_ooi_t nargs)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (MOO_OBJ_GET_FLAGS_RDONLY(rcv))
 | 
						if (MOO_OBJ_GET_FLAGS_RDONLY(rcv))
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		moo_seterrbfmt (moo, MOO_EPERM, "now allowed to change a read-only object - %O", rcv);
 | 
							moo_seterrbfmt (moo, MOO_EPERM, "not allowed to change a read-only object - %O", rcv);
 | 
				
			||||||
		return MOO_PF_FAILURE;
 | 
							return MOO_PF_FAILURE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -71,3 +71,18 @@ binary-argument := expression-primary unary-selector*
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#main
 | 
					#main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					================================================
 | 
				
			||||||
 | 
					#library Dynlib from 'dyn-lib.so'  <--- this is a generic shared library
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					## no instance variables allowed
 | 
				
			||||||
 | 
					## class-level(static) functions only.
 | 
				
			||||||
 | 
						int abc (int, float, void*)  <--- call this as Dynlib.abc (...). anc proper type conversion back and forth must occur.
 | 
				
			||||||
 | 
						int abc (int, float, void*) as abc: aaa x: x y: y  <--- remap the original name to a moo-style name.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#class abc  from 'x11.so' <--- this is a  moo-only shared library
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user