Sep 15 2008

Multithreading in BlitzMax

Published by at 7:57 am under Programming

Finally!

The latest SVN versions of BlitzMax have some basic support for multi-threading.

The following sample code is a remake of the old Alaska Software Xbase++ ‘coffee’ sample application. I haven’t used Xbase++ in quite a few years now, so I had to rewrite it based upon what I could remember of the coffee sample. Basically, it is about ten programmers who drink coffee while they are working and when their cups run empty, they go to a coffee machine and get themselves a refill. When the coffee machine runs empty, too, the guy who needs coffee has to cook some fresh one first. I’ve added to this simple scenario a button that will ‘close the office’ for the day and send the developers home. Before they do that, they finish their coffees and return their cups. The last one to do so turns off the coffee machine.

This idea is implemented using ten worker threads, one for each developer. They share one resource, namely the coffee machine. The purpose of the sample is to demonstrate mutexes as a means of synchronizing access to shared resources by concurrently running threads. As you probably already know, not synchronizing the access to shared resources is a perfect way to crash your application and giving you a hell of time to find the bug.

Here is the code, written on OS X, but it also works on Windows:

Download an application bundle for Mac OS X/Intel.

Download an application bundle for Mac OS X/PPC.

Download an executable for Windows.

I’ve begun to implement a thread class for BlitzMax. You can download it from here.

'GUI Coffee, Inc.
SuperStrict

'Put something on the screen.

Import MaxGui.Drivers
Import MaxGui.CocoaMaxGui
Import Pub.Threads
Import BRL.Random
Import BRL.Event
Import BRL.Eventqueue

AppTitle = "Coffee, Inc."

Global x:Int = 0
Global y:Int = 0
Global width:Int = 640
Global height:Int = 480

Global AppWindow:TGadget = CreateWindow (AppTitle, x, y, width, height)

Global fileMenu:TGadget = CreateMenu ("&File", 0, WindowMenu (AppWindow))
Global xit:TGadget = CreateMenu ("E&xit", 1, fileMenu)

Global helpMenu:TGadget = CreateMenu ("&Help", 2, WindowMenu (AppWindow))
Global about:TGadget = CreateMenu ("&About...", 3, helpMenu)

UpdateWindowMenu AppWindow

Global devStatus:TGadget[10]

For Local n:Int = 0 To 9
	devStatus[n] = CreateTextField (4, 4 + n*30, ClientWidth (AppWindow) - 8, 24, AppWindow)
Next

Global goHome:TGadget = CreateButton ("Go home, guys!", 240, 400, 140, 24, AppWindow, BUTTON_OK)

'Setup the office environment.

Const cupSize:Int = 200

'Your simple Italian coffee machine. One load is good for eight cups.
Type CoffeeMachine

	Field hasCups:Int = 10
	Field hasCoffee:Int
	Field isCooking:Int
	Field myMutex:Int

	Method Create()
		hasCoffee = 0
		myMutex = CreateMutex()
	End Method

	Method giveCoffee:Int( name: String, nPos:Int )
		LockMutex( myMutex )
		If hasCoffee <= 0
			Local boiler:String = ""
			For Local n:Int = 1 To 8
				boiler :+ "+++"
				SetGadgetText( devStatus[nPos], name + " is cooking fresh coffee: " + boiler )
				Delay 300
			Next
			hasCoffee = 8 * cupSize
		End If
		SetGadgetText( devStatus[nPos], name + " takes coffee." )
		hasCoffee :- cupSize
		Delay 750
		UnlockMutex( myMutex )
		Return cupSize
	End Method

	Method giveCup( name: String, nPos:Int )
		LockMutex( myMutex )
		SetGadgetText( devStatus[nPos], name + " takes a cup.")
		hasCups :- 1
		Delay 400
		UnlockMutex( myMutex )
	End Method		

	Method returnCup( name: String, nPos:Int )
		LockMutex( myMutex )
		SetGadgetText( devStatus[nPos], name + " returns his cup to the kitchen.")
		hasCups :+ 1
		Delay 1000
		If hasCups = 10
			SetGadgetText( devStatus[nPos], name + " is last, he turns off the coffee machine.")
			Delay 3000
			HideGadget( goHome )
		End If
		UnlockMutex( myMutex )
	End Method

End Type

'A very simple software developer. He only works as long as he has coffee.
Type Developer
	Field Name:String
	Field Position:Int
	Field hasCoffee:Int
	Field machine:CoffeeMachine
	Global EOB:Int = False

	Method Create:Developer( name:String, pos:Int, machine:CoffeeMachine )
		Self.Name = name
		Self.Position = pos
		Self.hasCoffee = 0
		Self.machine = machine
		Return Self
	End Method

	Function SignalEOB()
		SetGadgetText( goHome, "Office is closing!" )
		DisableGadget( goHome )
		EOB = True
	End Function

	Method Working()
		machine.giveCup( name, Position )
		Repeat
			hasCoffee :- Rand(1, Int(cupSize/4) )
			If hasCoffee <= 0
				SetGadgetText( devStatus[Position], name + " needs coffee !!!")
				hasCoffee = machine.GiveCoffee( name, Position )
			Else
				Local cupContents:String = ""
				For Local n:Int = 1 To Int( 1 + hasCoffee/10)
					cupContents :+ "#"
				Next
				SetGadgetText( devStatus[Position], name + " is happily working. Coffee left: " + cupContents + " ( " + hasCoffee + " ml)." )
			End If
			Delay 600
		Until EOB
		machine.returnCup( name, Position )
		SetGadgetText( devStatus[Position], name + " went home.")
	End Method
End Type

'The thread factory
Function BuildThread:Object( data:Object)
	Local myDev:Developer = Developer(data)
	myDev.Working()
End Function

'Start the coffee machine
Global Luigi:CoffeeMachine = New CoffeeMachine
Luigi.Create()

'Get the guys to work
Global DevTeam:Developer[10]
Global devName:String[] = [ "Mark", ..
							"Brucey", ..
							"Skidracer", ..
							"SebHoll", ..
							"SimonH", ..
							"SimonA", ..
							"FlameDuck", ..
							"MarkT", ..
							"RobH", ..
							"Woz"]

For Local n:Int = 0 To 9
	DevTeam[n] = New Developer
	DevTeam[n].Create( devName[n], n, Luigi )
Next

'Ready, steady, go!
Global handles:Int[10]

For Local n:Int = 0 To 9
	handles[n]=CreateThread( BuildThread,  Object(DevTeam[n]) )
Next

'Send the boys home
Function ShutDown()
	Developer.SignalEOB()
	For Local n:Int = 0 To 9
		DetachThread( handles[n] )
	Next
End Function

'Your good old fashioned GUI event loop
Repeat
	Select WaitEvent ()
		Case EVENT_GADGETACTION
			If (EventSource () = goHome)
				ShutDown()
			End If 

		Case EVENT_WINDOWCLOSE
			ShutDown()
			End

		Case EVENT_MENUACTION
			If EventData () = 1 Then
				ShutDown()
				End
			End If

			If EventData () = 3 Then Notify ("Coffee, Inc. only works with coffee!")

	End Select
Forever

6 responses so far

6 Responses to “Multithreading in BlitzMax”

  1. Maurice Z.on 15 Sep 2008 at 5:03 pm

    wow, whoa, WOW! And I thought they weren’t going to do so (Albeit I never looked at the latest posts about Multithreading in Blitz forum). Let’s check this out…
    Heh, I am pretty sure these “developers” are more like Coffeedrinking-machines.

    Anyway, sounds nice, even though I don’t think I will have any use of it, at least not until it gets into some late beta, so I can use it easily without having to worry too much.

    But still, it’s good to know that they have decided to work on it!

  2. Winnion 15 Sep 2008 at 10:50 pm

    Yes, those developers are like the ones from the old IBM advertisement campaign where they administered coffee intravenously to the Java developers – absolutely hilarious!

    This new multi-threading feature probably won’t be too useful for most game developers. But for application development, it’s been due for almost too long now (at least in my book, hm).

    Anyway. I’m now tempted again to use BlitzMax for something ‘real’. And maybe I will convince myself to write that book about BlitzMax the programming language… *sigh* 😉

  3. Bruceyon 06 Oct 2008 at 8:20 am

    Nice example, although I’m more of a tea drinker 😉
    Always find it quaint, looking back on MaxGUI event handling…

    Shame shame tho… perhaps a bout of staying away from “general” would be preferred than jumping ship. We aren’t all bad news, you know :-p

  4. Winnion 06 Oct 2008 at 11:54 pm

    Yes, MaxGUI’s event handling is, hm, very ‘basic’ and sort of a relict of the 90s. Still, MaxGUI has the advantage of using a Cocoa-back-end, and that easily is a killer argument on the Mac, especially with Carbon being an evolutionary dead-end.

    Visiting a forum is a bit like visiting a pub; when there are too many bullies around, I won’t go there again but find myself another place to spend my time. Especially when certain forum rules obviously don’t apply for everybody. Other folks have been banned for less while certain persons seemingly are allowed to post whatever they want in whatever language they want to use. I don’t accept two class societies, and I certainly don’t tolerate being repeatedly personally insulted. I know that others received the same ‘treatment’, but I can only speak for myself here.

    I was actually thinking on writing a reference and introductory book for BlitzMax, because I now have the time for that and need a project that I can finish in something like half a year or so. (Which, by the way, was also the reason why I was so interested in building wxMax on all three platforms myself.)

    For such a project, you need a lot of community support.

    However, I have also quit my last job because I was suffering from what you could call burn-out symptoms and there also was a constant negative stress level involved caused by certain co-workers that became too much for me.

    I no longer have sufficient stress tolerance, and I still have not rebuilt my ‘buffers’.

    When I begin to have headaches again when I read forum posts, I know that I have to leave it behind me.

    As it is now, chances are that I might begin working on an introductory book for Python instead.

    BlitzMax would have been my emotional choice for that project, because my affinity to BASIC languages goes back to the early eighties and BlitzMax offers a lot of nostalgia that I find attractive. But with the current atmosphere in the community and with my personal background, it is no longer any good for me and I better keep a distance.

  5. Steffen F. Pirsigon 23 Oct 2008 at 9:12 pm

    Hi Winfried,

    just a quick note, the coffee-cup sample was not
    written by Hannes. The original author was Joerg
    Witzel. Hannes just added the german comments
    to the sample based on the engl. comments made.

    Just for clarification purpose -:)
    regards
    Steffen

  6. Winnion 24 Oct 2008 at 6:44 am

    Hi Steffen – good to hear from you! Thanks for the info, I always thought it was from Hannes.

    All the best,
    Winni