|
|
|
Aug 29th, 2007.
|
Open Transport Squeak Notes
|
Things of interest, a collection of notes about the new Open Transport based network Plugin for Squeak for others that follow, or for others that want to know more, I'll cover the basics first then dive into more detail from the Open Transport perspective, and on another page give some performance numbers followed by information on the test Server , the test Suite. and a peek at Socket options
{Note in mid 2000 I migrated the Macintosh Carbon VM to the Unix Socket code after the port to OS-X}
I do have a copy of the code here, but the offical version should be taken from the Squeak image since this example might become dated
First all about the three semaphores. I took the viewpoint that we need to support old images running applications today. Therefore there is behavior there that we must live with. When I looked at the existing code base, say a 2.7 image I found:
- There is no accessor for semaphore, people could have added their own but I think few have done that, so I was not concerned about people writing their own logic to manage reading and writing using the single semaphore. Rather how does the existing code behave. (Of course if you have done this, then oops sorry)
- The only place in the image where we wait on the semaphore with intent to wait for incoming data is Socket>>waitForDataUntil:
Socket>>waitForDataUntil: deadline
"Wait up until the given deadline for data to arrive.
Return true if data arrives by the deadline, false if not."
| dataArrived |
[self isConnected &
(dataArrived _ self primSocketReceiveDataAvailable: socketHandle) not
"Connection end and final data can happen fast, so test in this order"
and: [Time millisecondClockValue < deadline]] whileTrue: [
semaphore waitTimeoutMSecs: (deadline - Time millisecondClockValue)].
^ dataArrived
In this routine I realized there is a guard clause (self primSocketReceiveDataAvailable: socketHandle) that is used to determine if we are going to wait on this semaphore. Keeping in mind that we still might have a large code base that won't upgrade their image, but might upgrade their VM, my thought was to drive the semaphore signaling based on the Smalltalk code, versus on the behavior of the support code or semantics from BSD or OT and make everyone change their Smalltalk code to fit a new plugin signalling model
In my implementation we only arm the semaphore after seeing the primSocketReceiveDataAvailable and responding there is no data. We know that the next thing that will happen (baring the deadline timeout) is that the process will be waiting on the semaphore.
This follows Craigs thoughts of telling the support code that we are going to wait on a semaphore so be ready to trigger it.
When I was chasing the external semaphore table issue my instrumented VM showed quite a number of times where I armed the semaphore and 1 microsecond later I would get data and signal the semaphore.The same logic applies to the write semaphore.
- It is only used once
- There is a guard clause self primSocketSendDone: socketHandle that implies intent to signal the semaphore
waitForSendDoneUntil: deadline
"Wait up until the given deadline for the current send operation to complete. Return true if it completes by the deadline, false if not."
| sendDone |
[self isConnected & (sendDone _ self primSocketSendDone: socketHandle) not
"Connection end and final data can happen fast, so test in this order"
and: [Time millisecondClockValue < deadline]] whileTrue: [
semaphore waitTimeoutMSecs: (deadline - Time millisecondClockValue)].
^ sendDone
The callers tend to invoke this method, if successful then they send data.
Right now if you ask me if the send is done and we are in flow control state, I say no and I know baring the timeout check that the next thing that will happen is the Smalltalk code will be waiting on the semaphore, so when flow control is lifted I signal the semaphore.
Now for some more detail from the OT perspective
The Read Semphore logic:
When data is being read the plugin either points to the read semaphore for the instance of socket, or to the single socket semaphore depending on the version of the class you are using, this is to support old code and behavor
The Read Semaphore is signaled when data arrives at the interrupt level for a socket and the trigger is true, the trigger is reset.The Read Semaphore trigger is set when the user calls sqlSocketReceiveDataAvailable and we do NOT have any data waiting to be read we of course return false from the routine.
The Smalltalk class library primSocketReceiveDataAvailable method is called from
Socket>>dataAvailable
Socket>>waitForDataUntil
In waitForDataUntil if you have no data then you wait on the readSemaphore. This infers that intent since other places call dataAvailable and may or may not wait on the semaphore.
ReadSemaphore is signaled as part of signaling all three semaphore logic explained lower down.
The WriteSemaphore logic:
Again thee plugin either points to the write semaphore for the instance of socket, or to the sole socket semaphore depending on the version of the class you are using.
The WriteSemaphore is signaled when:
- Flow control for writing to the socket is lifted and we had marked the socket as under flow control and the trigger is true, the trigger is reset.
- An orderly release arrives and we had marked the socket as under flow control and the trigger is true, the trigger is reset. (This is a special case to fix some issues in OT)
WriteSemaphore is signaled as part of signaling all three semaphore logic explained lower down.
WriteSemaphore trigger is set when the user calls sqlSocketSendDone and we are under flow control for the socket. Flow control is set when the get a flow control error when writting to the socket, or writting is blocked because of a pending orderly release, or T_GODATA or pending disconnect.
The Smalltalk class library sqlSocketSendDone method is called from:
sendDone
waitForSendDoneUntil:
In waitForSendDoneUntil if data is still pending to be sent you wait on the writeSemaphore. This infers that intent since other places call sendDone and may or may not wait on the semaphore.
The non-read/write semaphore points to the semaphore for the instance of socket.
This semaphore is signaled if the trigger is true when the socket goes into unconnected state, or goes into connected state, or as part of the three semaphore trigger explained lower down.
A socket goes unconnected if:
the bind to a port fails when binding
the connect to a host fails
A listen socket which does a accept has the accept fail.
A T_PASSCON event arriving for a socket that we have done accept for fails.
A listen socket gets a bind failure
a UDP socket gets a bind failure
a socket goes connected if:
the socket gets a T_ACCEPTCOMPLETE event
the socket actually connects without error.
a T_PASSCON event happens on a accepting socket.
a T_LISTEN event happens on a listening socket.
a bind event happens on a udp socket.
The need to signal flag is sent when
sqSocketAcceptFromRecvBytesSendBytesSemaID is called.
sqSocketCloseConnection is called.
Or when socket is bound to a port. This binding occurs when
sqSocketListenOnPort
sqSocketListenOnPortBacklogSize
sqSocketAcceptFromRecvBytesSendBytesSemaID
sqSocketConnectToPort
All three semaphores can be triggered when:
a) sqSocketCloseConnection is called and the trigger is true, the trigger is reset.
b) An abortive disconnect arrives on the socket at the interrupt level and the trigger is true, the trigger is reset.
c) An orderly disconnect arrives on the socket and we have orderly closed our side and the trigger is true, the trigger is reset.
d) Our abortive disconnect completes at the interrupt level and the trigger is true, the trigger is reset. (This is redundant see (a) above).
e) Your macintosh sleep method is called and the trigger is true, the trigger is reset. This is a special case since in reality the tcp/ip layer is going away. The behavior of the old plugin is to crash the machine. The new plugin triggers the semaphore, and marks the private handles as destroyed. When you awake well you've got a bunch of invalid socket handles. However in theory your Smalltalk logic is checking for isConnected and takes action.
|