The Artima Developer Community
Sponsored Link

Agile Buzz Forum
Day of Delays

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
James Robertson

Posts: 29924
Nickname: jarober61
Registered: Jun, 2003

David Buck, Smalltalker at large
Day of Delays Posted: Feb 1, 2006 11:20 PM
Reply to this message Reply

This post originated from an RSS feed registered with Agile Buzz by James Robertson.
Original Post: Day of Delays
Feed Title: Travis Griggs - Blog
Feed URL: http://www.cincomsmalltalk.com/rssBlog/travis-rss.xml
Feed Description: This TAG Line is Extra
Latest Agile Buzz Posts
Latest Agile Buzz Posts by James Robertson
Latest Posts From Travis Griggs - Blog

Advertisement

This started Monday, but I didn't get back to it and have it working right till today. It started out with this post about Andeles (unDelay) a while back.

Monday I had some tests that were taking a long time because they have to wade through Delays. It's not easy code to separate out and make easily testable, it's a bunch of Socket based code that actually takes advantage of Socket IO with timeouts.

The problem is that "delays" with VW IOAccessors are "special". You can see the code in readWaitWithTimout: aMilliseconds. This is basically the equivalent of a C library select() call. How its done, is that a Delay is created, they then reach inside the delay, grab it's semaphore, and hand it to the select call so that the select call can notify it when IO is available. If the Delay expires first, it will beat the select() call to it and let the thread charge on. In this way, they keep all the timing based delay code centralized and right in Smalltalk where you can see it.

The original code basically checked to see if a process was blocked by a semaphore, and then stepped back up the chain to see if someone had sent wait to a Delay. In this case though, that doesn't happen. It is a Semaphore that is being waited on, though a Delay has been setup on the side to trigger it. So I had to robustify the code a little.

The new version add this method to Delay class:

delayForSemaphore: aSemaphore 
	^AccessProtect critical: 
			[ActiveDelay isNil 
				ifTrue: [nil]
				ifFalse: 
					[ActiveDelay delaySemaphore == aSemaphore 
						ifTrue: [ActiveDelay]
						ifFalse: 
							[SuspendedDelays detect: [:each | each delaySemaphore == aSemaphore]
								ifNone: [nil]]]]

The idea is to look amongst the active delays registered with the system, and find the one (if any) that hosts a given semaphore. Delays that are in progress are in either the ActiveDelay or in SuspendedDelays. Then we can add unDelay to Delay:

unDelay
	delayInProgress ifFalse: [^self].
	AccessProtect critical: 
			[ActiveDelay == self 
				ifTrue: [TimingSemaphore signal]
				ifFalse: 
					[SuspendedDelays remove: self.
					delayInProgress := false.
					delaySemaphore signal]]

If our delay is the ActiveDelay (this means is it the soonest scheduled to expire), we just kick the TimingSemaphore now. Said semaphore had been armed to be kicked by the VM when the delay time expired, but we're not interested in waiting, so we kick it prematurely. The downstream result of that signal, will make sure everything is managed correctly, even though it triggered "early". If it is scheduled to happen later than the soonest, we take the other branch. Remove it from SuspendedDelays (all waiting delays other than the soonest), mark it as not in progress, and then trigger the delay's semaphore. This is the semaphore the Delay uses when you send wait to the delay. The important part actually, is that delayInProgress := false. My first attempt at making this work with the IO timeouts, continued to deal with the semaphore directly. The problem is that in the readWithWaitWait: code, after it gets past the semaphore, it checks the status of the delay and infers where the singal came from. Since I was just mucking with the semaphore directly, it incorrectly assumed it was an IO signal, not a timeout. This is what drove hunting down the containing Delay of a blocking semaphore and getting it to do the unblocking. The final piece, is to rewrite the original Process unDelay code as such:

unDelay
	suspendedContext ifNotNil: 
			[:sc | 
			sc receiver class == Semaphore 
				ifTrue: 
					[| delay |
					delay := Delay delayForSemaphore: sc receiver.
					delay == nil ifFalse: [delay unDelay]]]

With this in place, we can unDelay a process at any time, whether it be "normal" delay code, or that which uses IOAccessor timeouts, or any other similar mechanism. In fact, a handy helper to make tests run "faster" if we aren't really interested in waiting for delays is something along the lines:

smashThroughDelaysWhile: aBlock 
	| done thisProcess |
	done := false.
	thisProcess := Processor activeProcess.
	
	[[done] whileFalse: 
			[(Delay forSeconds: 0.1) wait.
			thisProcess unDelay]] 
			forkAt: thisProcess priority - 1.
	^aBlock ensure: [done := true]

Read: Day of Delays

Topic: Smalltalk Experience at LWNW/Smalltalk Solutions Previous Topic   Next Topic Topic: Is Dumbledore really dead?

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use