1. Processes

A process contains one or more threads. It holds information that is common for all of its threads. Each process has a main thread and can have multiple child threads.

A process itself has no id, it is identified by the id of its main thread.

2. Threads

A thread is a single stream of execution. g_task is the data structure that contains information for a thread.

Threads running in kernel-level only have one stack, user-space threads have an additional stack that is used during interrupt handling. The stack section describes how these stacks are handled.

2.1. Types

The following constants of type g_thread_type denote types of threads.

Identifier Description

G_TASK_TYPE_DEFAULT

Normal thread

G_TASK_TYPE_VM86

Virtual 8086 thread

2.2. Kernel-level

2.2.1. Creating a thread

In kernel space, a thread can be created by using taskingCreateTask and assigning it to scheduling with taskingAssign.

2.2.2. Considerations

There are some things to consider when implementing a thread in kernel-level. When a kernel-lock is taken by a task, scheduling is disabled until the task releases the lock. This is inevitable as it could cause a deadlock if the task is interrupted while holding a lock.

To minimize any blocking, locks should therefore be used around critical parts but acquired as late and released as soon as possible.

2.2.3. Exiting a thread

A kernel thread must be exited using taskingExit, otherwise the task will run into nirvana and cause a failure.

2.3. User-level

2.3.1. Creating a thread

To create a thread from userspace, the g_create_thread API function is used. This function must be supplied with an address pointing to code that the thread shall execute. Additionally to the entry point, a pointer to user data can be passed.

A simple example could look like this:

void myThreadedCounter(int* counter) {
	for(;;) {
		(*counter)++;
	}
}

int main(int argc, char** argv) {
	int counter = 0;
	g_create_thread_d(myThreadedCounter, &counter);

	for(;;) {
		g_sleep(1000);
		printf("The counter is: %i\n", counter);
	}
}

Each thread has a unique id of type g_tid. This id can be obtained from within the thread using g_get_tid.

2.3.2. Internal initialization

The thread initialization sequence uses a wrapper function for starting the thread. First, the G_SYSCALL_CREATE_THREAD system call is called and supplied with a "thread setup routine" and the "user entry" (the function given to g_create_thread).

This setup routine is the first piece of code that is executed from within the newly created thread, but does initially not know where to start execution. To get this information, it uses G_SYSCALL_GET_THREAD_ENTRY which returns the user entry to start executing at.

This entire procedure is done to avoid that the kernel must modify the user stack to put the necessary values on it, but rather do it from within code which is inherently ABI compatible.

2.3.3. Exiting a thread

A thread is automatically destroyed once the entry function finishes execution. To stop execution from an arbitrary point, the g_exit API function can be used.

A single thread can not be killed from a different process; using the g_kill always causes the entire process to exit.

When the main thread of a process dies, all threads of the process are killed.

2.4. Security Levels

When creating a process, a security level is used to determine what permissions the threads of the process have. This security level is stored on the process and applied on thread creation.

2.4.1. Existing security levels

The following constants of type g_security_level denote the different security levels.

Identifier Description

G_SECURITY_LEVEL_APPLICATION

Has no specific permissions.

G_SECURITY_LEVEL_DRIVER

Has I/O permission level 3 (may use out and in commands to access the CPU I/O ports) and is permitted to use a variety of driver-related system calls.

G_SECURITY_LEVEL_KERNEL

Runs in kernel space