Idea for event based thread support

BlitzMax Forums/BlitzMax Programming/Idea for event based thread support

Blueapples(Posted 2008) [#1]
Okay so here's my idea for threading in BlitzMax. Mark, please take a look and see what you think. You said in a previous thread that you wanted an elegant solution that was something non-standard. I think I've got something that fits that bill here.



The idea is that once a thread is kicked off, the only way to communicate with it is through events and an event loops.

So in order to control threads we define these new commands:

ThreadStart:Int(ThreadObject)              ' Returns an integer ID for the thread
ThreadStop ID:Int                          ' Stop the thread ID given
ThreadEmitEvent ID:Int, Event:TThreadEvent ' Send an event to a thread. The only way for threads to communicate.


A thread object is descendant from TThread and provides a Run method that is called by StartThread. Each thread is given it's own copy of the Garbage Collector.

When ThreadEmitEvent is called, a copy of the Event object is created by the Garbage Collector for the thread ID provided, then placed on that thread's event queue. This thread can then pick up the event object at it's convenience: this assumes that a thread that expects to receive events will be running an event loop.

To solve the object copying problem, we define a "thread safe" event object which can easily be copied using a small algorithm (validating this and doing the copying would be supported by introspection or at a lower level in the C code more likely).

TThreadEvent
- All fields must be of one of these types:
- A simple number
- String object
- Another TThreadEvent descendant

This gives a well defined format that we can easily copy.

I don't know what the Garbage Collector looks like internally, but I assume that this could work. Basically, threads can be looked at from the perspective of the GC as an external hardware resource: they simply generate events that can be read by other threads. In principal this should require no significant changes to the GC because it only manages the objects it allocates memory for: a thread cannot directly pass an object to any other thread, or make calls to functions in other threads so multiple GCs cannot step on each other's toes.

Edit: fixed some mistakes


Koriolis(Posted 2008) [#2]
I think this is an promising idea.
In fact I myself investigated possible threading schemes that would not have the major drawbacks of mainstream threading APIs (mainly, that the default for a thread is to share all the data with other threads, which is a synchronization nightmare and just doesn' makes sens).
The message passing scheme is precisely one of my favorites.
However the main problem in a language like BlitzMax is very likely to be with any code that has global side effect. It's one thing to forbid access to global variables from threads, it's another to forbid access to functions like Print. But if you don't, you aren't any safer. What happens if two threads call Print simultaneously? Or more generally, what happens if they call functions that access global variables or worse, call C code that is not thread safe ?
The very problem of most thread APIs comes from the language: if the language allows unrestricted side-effect (that is, any mainstream language) then threading secrity simply cannot be guaranteed by the language itself, and all the burden goes to the programmer. Current threading handling is really primitive, it's a bit like looking at assembly code when you know higher level languages. No in fact it's worse, I can enjoy assembly programming, but I hate threading APIs.

EDIT: alternatively, if serializing was implemented by a standard blitzmax module, this could be used to pass copies of practically *any* object to the threads (with the only drawback that you'd have to be very carefull no to pass huge graphes of objects this way, as serializinf is recursive).


ImaginaryHuman(Posted 2008) [#3]
I don't think there is such a thing as totally safe threading, is there? As soon as you split a program into more than one piece you inevitably must have the potential for collisions unless they are totally permanently isolated from each other, which makes their access to the same data impossible. To share access one or the other thread has to be put on hold - a lock of some kind, to prevent access. So I don't think we really need to worry about totally safe threads, it's not doable, but you can at least aim for temporary lock-out (which actually temporarily defeats the idea of threading, funnily enough).

That said it would still be *really* cool to have more than one thread in BlitzMax, even if it was only a total of 2 threads. I think perhaps theads should be implemented in a way that is not *totally* safe, to begin with, so people who feel comfortable can use it sooner, and then later refine it to be safer.


Dreamora(Posted 2008) [#4]
Threads don't communicate.
They do their task and end, giving you the result.

As soon as you start "communications" threads would need to be able to run asyncronous within themself to handle the event like an interupt.
Thats not really the point of it, thats what scripts are for if you need it.

But a thread needs to run self contained. Use a thread manager if you need to change stuff on a thread. but that again would need threads to be objects so you could use setglobal, setlocal or whatever to alter stuff within the threads "memory space"

Your idea is more of a thing for which pub.freeprocess, TProcess.pipe and stdinStream / stdoutstream are used for.


Winni(Posted 2008) [#5]
BlueApples' idea is basically a subset of what Alaska Software's Xbase++ does.

In Xbase++, multithreading is implemented through thread classes, synchronization is done via methods with a "SYNCHRONIZE" attribute and there are also SYNCHRONIZE properties to ensure that the access to them is being serialized and cannot occur at the same time.

Furthermore, each thread object in Xbase++ has an own event queue (and an own work space for its open databases). You can send events to an other object's event queue with the PostAppEvent() function.

It is possible to make a running thread object persistent and write it to a database; other Xbase++ applications running on different computers will receive a notification (in form of an event) from the database engine that the state of the database has changed, and if you want to, you can retrieve the thread object on a different computer and continue its execution there. We actually had this feature already in 1998. (I've worked there from the summer of '98 'til the beginning of 2001, in case you wonder about the 'we'.).

Of course, the whole thing is also fully garbage collected. By the way, Yellow Cab in New York are using Xbase++ for their dispatch application.


Difference(Posted 2008) [#6]
Threads that can't use Bmax objects would be of little use to me.

I'd like processes that will run in parallel and communicate with each other, polling and exchaning states and data.

An event based system sounds fine too me.


CoderLaureate(Posted 2008) [#7]
If we had Public/Private access modifiers threading would be a breeze in BMAX.


marksibly(Posted 2008) [#8]
Hi,

Some nice ideas there, but I'm now 87% certain I'll be tinkering with the GC system soon in an attempt to at least make brl.blitz thread safe.

In fact, the reflection stuff in there at the moment means it's almost possible to write a custom GC, except that the system doesn't yet have a list of root (ie: 'Global') objects.

This will likely be an ongoing mission. There are many other things that will need to be taken into account (esp: exceptions) but I think by taking things a step at a time we'll get there eventually.


ImaginaryHuman(Posted 2008) [#9]
Yah and threads also run on multiple CPU cores which means they can run in parallel.


Blueapples(Posted 2008) [#10]

alternatively, if serializing was implemented by a standard blitzmax module, this could be used to pass copies of practically *any* object to the threads (with the only drawback that you'd have to be very carefull no to pass huge graphes of objects this way, as serializinf is recursive).



I kind of like that idea. It seems like you might end up requiring a very simple structure like I defined as TThreadEvent, but if we used generic serializing functions it would be possible to use any class as long as it could be serialized (for instance, object have to provide their own implementation of serialization in MFC).


Like some are saying, I don't think the core language *can* be thread safe. If two threads try to print to the same console, what happens? Well, the app crashes. Period. I really honestly don't have a problem with that. If I need two threads to output text, they will send a message to the main loop and it will do the output. We can't isolate programmers from their own stupidity or lack of knowledge of thread-safe operations, though my system goes a long way in doing that I think.


Threads don't communicate.
They do their task and end, giving you the result.



While that's certainly true of some threads, it isn't always the case. Network threads often will stay running for the entire application lifetime, for instance, to handle messages coming and going.


Your idea is more of a thing for which pub.freeprocess, TProcess.pipe and stdinStream / stdoutstream are used for.



Uh, no. While this could be partially implemented using those libraries, that does not in any way replace the utility of having actual threads within the same app. The only way those could be used as-is to create anything similar would be to have multiple actual processes simulating threads, which is quite expensive on most platforms.


Winni, XBase++ sounds interesting. I'm going to look it up. I'd really like to see this kind of thing in BM.

Mark,

Well if you do come to that I'd be very keen to see it. I'll be the first to do testing if you like. I wonder if something like this might be good as a partial solution, if the implementation would be faster than making the GC thread safe.


Blueapples(Posted 2008) [#11]
Just occurred to me that if we get a usable threading system running in BlitzMax or if Mark makes the language thread safe, I pretty much will never switch away from it. There'd be no need at that point, it would literally have everything I need.

That's kind of a crazy cool thought. So very close. :)