How To Cog - Chapter 3.6 - Threads

A thread is a programming term for an instance of the flow of control through code. You can have more than one thread at a time going through the same code. Cog's threads aren't the same as threads used by languages like C++. They're similar, and this tutorial borrows the term.

How They Work

When a cog receives an event that it has a handler for, JK finds that handler's label in the code section and code execution begins. But what if that handler contains a sleep and another message is received before the sleep ends? And what if it's the same event?

Threads are the answer to that problem. Cog is capable of having multiple threads for one cog, but JK will only work with the last-created thread. Here's an example for the above scenario:

activated:
	if(on) return;
	on = 1;
	channel=PlaySoundLocal(yourSnd, 1, 0, 0); 
	Sleep(GetSoundLen(yourSnd));
	on = 0;

return;

Say a player activates a console and this activated handler is run. The handler first checks to make sure it finished playing the sound from the last time it was run. If the player activates the console twice in succession before the sound finishes, the variable "on" will still have a value of 1, and the second thread will die with that return statement.

You can have up to five threads for each cog. Threads are local to their cogs - meaning that if one cog has five threads, it won't stop another cog from having its five threads. Whenever a cog needs to create a new thread in response to an event and it's already at its max, the oldest thread that the cog has will die - or in some cases the new thread will not be created (as in a call).

Say that you have a lot of calls to carefully encapsulated messages in your code. But you've noticed after some recent changes that some of your calls aren't working. You need to check to make sure you don't have more than four calls going at once (the original event handler has a thread too).

Be careful when using sleeps that the sleep's handler won't be called again before the sleep ends. Otherwise, the thread limit will be exceeded, and the cog will wait for the sleeps to end before doing anything else. When JK looks at a cog to see if it needs to do anything (like wakeup from a sleep), it will only look at the last thread. So if the cog's first four threads slept for only a half second, but the fifth thread slept for five, the cog would wait five seconds before going back to the first four threads.

If it's impossible for you to avoid too many threads building up (or indefinite sleeping), JK provides the Reset() verb to kill all threads except the current one. This can be a good safeguard, but if your code is well-written, you shouldn't have to use it.

Affected Verbs

Calls and sleeps are the main causes of thread problems, but any verb that causes another thread to be created in its cog can cause the same problem.

When you use SendTrigger() to send a trigger out, this event message is sent out to all of the local cogs that are listening - including the cog that sent the trigger. Whether that cog's trigger handler is looking for that sourceref or not, a thread is still created for the handler - if you've already got some sleeps or calls going, two more threads (the thread that ran SendTrigger() and the triggered thread) may take you over the limit.

This is true of any verb that waits until all local cogs have had a chance to answer its event message. If you use CreateThing() in the class cog of the thing that's created, or if you use SendMessage() to send a message within a cog, you'll have the same problem as with SendTrigger().

Alternatives

Instead of creating extra threads and waiting on them, you should use timers and pulses when you can. Timers and pulses are very similar in implementation and neither of them takes up a thread while they're waiting to run.

Although sleeps cause a lot of problems, they aren't unstable or error-prone. The above example, makes good use of a sleep, and there's no reason to switch to a timer. But in most cases, there's nothing stopping events from happening almost simultaneously - so even if you have a sleep of a tenth of second, there's still a chance of hitting the thread limit.

Imagine that there are ten fuel barrels in your level, and you're standing not too far away with a bryar. A few shots from you and all of those barrels will blow within a tenth of a second. If the barrels' class cog has a sleep for the slight pause before the explosion, then not all of the barrels will explode correctly. But if timers are used (and you can have a ton of timers), then everything will blow up nicely.


Previous: Multiplayer Up: How To Cog Next: Flags
  • Create:
This page was last modified 16:24, 14 April 2006.   This page has been accessed 1,722 times.