ThreadJc

ThreadJc

Inhalt


Topic:LibJc.Threads


1 Threads in Java

Topic:LibJc.Threads.JavaThreads

Java hat bereits in der Version 1.0 eine vollständige Threadunterstützung enthalten. In der Version 1.1 erfolgenten dann noch einige kleine aber wesentliche Korrekturen in der Hinsicht, dass bestimmte Methoden als deprecated und unrecommended bezeichnet wurden, womit wichtige Anwendungserfahrungen reflektiert wurden. Ansonsten ist das Thread-System von Java sehr stabil.

Folgende wesentliche Klassen und Funktionalitäten bietet Java:

Mit diesen Basisfeatures ausgerüstet sind in Java alle Belange von Multithreading aus dem Anwenderbereich heraus programmierbar. Nicht enthalten ist die Formulierung von Interrupts und der Übergang von Interruptroutinen in die Thread-Ebene. Das sind jedoch Probleme der Hardwareanpassung (Treiber), die nicht in die Aufgabenbereiche einer Java-Programmierung und auch nicht in die allgemeine Anwenderprogrammierung gehören.


2 Thread-Kapselung in der CRuntimeJavalike

Topic:LibJc.Threads.ThreadsInCRuntimeJavalike

Die Arbeit mit Threads ist mit der CRuntimeJavalike oder JcLib gegenüber dem speziell ausgeprägten Aufrufen der Thread-Schnittstellen eines Betriebssystems gekapselt. Die Funktionalität entspricht dabei dem System des Java. Lediglich Bezeichnungen und Aufrufkonventionen sind der Programmierung in C angepasst.

Die Kapselung enthält noch ein weiteres Layer: OSAL (Operation System Adaption Layer). Der Grund dafür ist: Nur die allgemein notwendigen Anpassungen sollen vorgenommen werden. Nebeneffekt: Es wird eine betriebssystemunspezifische OSAL-Thread-Schnittstelle bereitgestellt, die auch außerhalb der CRuntimeJavalike verwendet werden kann. Das ist wichtig, da insbesondere im Treiber- und hardwarenahem Bereich direkt oft in C programmiert wird.


3 Schnittstelle zum Realtime-Betriebssystem (RTOS)

Topic:LibJc.Threads.OS_Interface


3.1 Threads basieren auf der Threadunterstützung des Betriebssystems$Threads are based on the os-system-layer

Topic:LibJc.Threads.OS_Interface.OS_Thread


Topic:LibJc.Threads.OS_Interface.OS_Thread.usingOsysSupport

Eine Java-Virtuelle Maschine ist in der Lage, nebenläufige Programmabarbeitung zu bieten, ohne dass das Betriebssystem dabei Multithreading unterstützen muss. Diese Eigenschaft ist in Zeiten entstanden als Windows noch kein richtiges Multithreading konnte, zudem soll Java auf möglichst vielen Plattformen laufen und darf daher nicht zuviel vom Betriebssystem abverlangen. Die Möglichkeit dieser Arbeitsweise liegt darin begründet, dass die Threadverwaltung kooperativ auf Userlevel erfolgte oder die virtuelle Maschine macht die Threadverwaltung. ((Allerdings wird in Java-VMs die Threadunterstützung des Betriebssystems auch genutzt, wenn eine solche verfügbar ist.))

Die Arbeitsweise ist im Embedded-Bereich nicht zweckbringend. Multithreading gibt es deshalb, weil dieses Multithreading in harter Echtzeit unter den Bedingungen des Betriebssystems erfolgen muss, mit Interrupt-Einbindung. Die CRuntimeJavalike oder JcLib enthält also keinen eigenen Multithread-Kern.

Vielmehr werden Schnittstellen der OS-Anbindung aufgerufen. Die OS-Anbindung ist als allgemeingültige Schnittstelle definiert, die Implementierung dieser Schnittstelle ruft die Routinen des Betriebssystems.und muss demzufolge angepasst werden.


Topic:LibJc.Threads.OS_Interface.OS_Thread.os_thread

Das Headerfile os_thread.h.html definiert die vom Betriebssystem abverlangten Schnittstellen. Deren Implementierung für Windows befindet sich in platform_Windows/os_thread.c.html. Eine Implementierung für andere Betriebssysteme muss ähnlich aussehen.


Topic:LibJc.Threads.OS_Interface.OS_Thread.threadRoutine

Die Thread-Routine ist diejenige Subroutine, die im Thread aufgerufen wird. Der Prototyp der Subroutine ist mit dem Typ OS_ThreadRoutine definiert. Der Routinen wird ein untypisierter Datenzeiger übergeben, dessen Struktur in der Routine dann bekannt sein muss, es wird gecastet.

Wenn die Threadroutine beendet ist, dann wird auch der Thread beendet. Eine Variantenvielfalt, die das eine oder andere Multithread- oder Multitask-Betriebssystem bietet, ist oft nicht nutzbringend verwendbar sondern kostet nur die Portabilität. Wenn beispielsweise eine Aufgabe beendet ist, dann könnte eine Threadroutine beendet werden, aber der Thread möge nach Wunsch von Entwicklern und Leistungsfähigkeit eines Betriebssystems noch startbereit bleiben um die Aufgabe, den Thread dann erneut starten zu können. Der Thread bleibt dann faktisch erhalten, nur ist er wartend. Das gleiche kann man aber mit dieser Schnittstellendefinition auch erreichen, in dem die Threadroutine nicht beendet sondern in wait for signal des Neustartes schickt und eine übergeordnete Schleife bildet:

 int threadRouine(void* data)
...
 do {
   dotheTaskOfTheThread();
   os_wait(handleWaitForRestart, mutex, -1);
 } while(!abort)

Dann braucht man keine speziellen Betriebssystembefehle und hat eine einheitlichere Handhabung vergleichbarer Probleme. Die oft angebotenen umfangreichen Möglichkeiten eines Betriebssytems braucht man defakto nicht, sie bieten hierfür keinerlei Vorteil.


Topic:LibJc.Threads.OS_Interface.OS_Thread.createThread

Die Anlage und der Aufruf eines Threads geschieht mit Aufruf der Routine os_createThread(handle, threadRoutine, userData, name, prio, stackSize). Diese Routine legt einen Thread im Betriebssystem an und startet ihn sofort. Einen extra Befehl für den Threadstart würde auch nur einen Sonderfall produzieren. Threads sollten in embedded-Systemen in der Hochlaufphase angelegt werden. Wenn die zugehörige Aufgabe nicht sofort gestartet werden soll, dann ist es besser, in der Threadroutine innen ein waitForStart einzuprogrammieren als Besonderheiten der Betriebssystem-Threadverwaltung zu nuzten. Letzteres bietet keine Vorteile sondern nur den Nachteil, dass eine Kontrolle des initialen ordnungsgemäßen Anlaufes aller Threads nicht erfolgt.

Der handle wird verwendet, um von außen etwas mit dem Thread zu tun. Ein OS_HandleThread ist als Zeiger auf eine unbekannte Struktur definiert und hat damit die Byteanzahl eines Zeigers. Es kann der Zeiger auf einen systeminternen Datenbereich sein, es kann sich aber auch nur um eine einfache Zahl handeln. Das kann implementierungsspezifisch sein. Die Verwendung eines Zeigertyps statt int ermöglicht dem Compiler den Typtest. Werden alle handle, egal welche, mit int typisisert,dann wird ein Verwechslungsfehler beispielsweise zwischen Handles von Threads und derjenigen von Files nicht bemerkt.

Die Threadroutine muss der obigen Definition entsprechen, sonst gibt es einen Zeigertypfehler beim compilieren.

Der Aufbau des dem Thread zugeordneten Datenbereiches kann aber nicht vor bestimmt werden. Es ist keine Verallgemeinerung ableitbar, daher void*.

Ein Threadname dient der Anzeige von Betriebssystemaktivitäten und hat keinen Einfluss auf den Softwarelauf.

Die Priorität ist initial und kann im Verlaufe des Threads geändert werden. Eine solche Änderung ist notwendig in einer critical section, wenn ein hochpriorer Thread wartet. Diese Eigenschaft muss von einem Multithread-Betriebssystem unterstützt werden,sonst taugt es nichts. Eine manuelle Prioritätsänderung ist vorgesehen, das ist allerdings eher Kür. Die Priorität wird hier mit einer Zahl zwischen 1 und 254 angegeben. Damit ist eine feinfühlige Priotitätsstaffelung möglich, die von Multithreadbetriebssystemen in dieser Feinausprägung nicht unbedingt unterstützt wird. Eine Prioritätsfestlegung muss sehr gewissenhaft erfolgen und kann gegebenenfalls im Verlaufe der Entwicklung noch feinjustiert werden.

Die Angabe einer Stackgröße ist der kritischste Parameter, da oft nicht die notwendige Stackgröße bekannt ist. Dieses Problem ist aber im Themenkreis der Multithread-Anwendungen bekannt.


3.2 User-ThreadContext

Topic:LibJc.Threads.OS_Interface.ThreadContext

Ein User-Threadcontext ist ein Speicherbereich, der außerhalb des OSAL beliebig definiert ist, in diesem Fall innerhalb der CRuntimeJavalike oder JcLib - Implementierung.

Für die OSAL-Schnittstelle bedeutet das zunächst nur, dass der Zeiger auf den User-Threadcontext und dessen Länge in der OSAL-Schicht vermerkt wird, und zwar im OS_ThreadContext oder anders geeignet. Die Verwendung des OS_ThreadContext ist kein muss, sondern eine Implementierungseigenschaft der vorliegenden OSAL-Implementierungen. Dieser Speicherbereich kann sowohl im geschützten Bereich des Betriebssystems liegen als auch in dem OSAL-Layer. Die Implementierung hängt von den Möglichkeiten des RTOS ab. Dieser Thread-Context ist OSAL-intern.

Der User-ThreadContext wird dem OSAL mitgeteilt durch die Methode os_setCurrentUserThreadContext(mem) definiert in OSAL/inc/os_Thread.h. Dabei wird in einer Struktur OS_ValuePtr definiert in der os_types_def.h Zeiger und Länge übergeben. Das Abfragen geschieht dann mit os_getCurrentUserThreadContext().


3.3 Kritische Bereiche, Mutex

Topic:LibJc.Threads.OS_Interface.OS_Mutex


Topic:LibJc.Threads.OS_Interface.OS_Mutex.generally

Kritische Bereiche (crtitical sections) sind diejenigen Code-Stellen, in denen eine Unterbrechung von anderen Threads Dateninkonsitenzen bewirken können bis zum Hängenbleiben des Systems. Der Begriff Gegenseitiger Ausschluss (mutual exclusion) eines Zugriffes ist hier noch prägnanter. In einfachen auf Interrupts basierenden embedded Programmierungen gibt es dazu die globale Interruptsperre, die einfach aber hilfreich ist. Fehler einer vergessenen Freigabe wirken sich allerdings fatal aus. Außerdem sind längere Interruptsperren ungünstig für die schnellen Interrupts. Daher ist in Multithread-Betriebssystemen die Interuptsprerre dem Betriebssytem-Kern vorbehalten. Für die Synchronisierung der Treads für den Mutex-Zugriff gibt es selektiv wirkende Mechanismen


Topic:LibJc.Threads.OS_Interface.OS_Mutex.os_mutex

Auf OSAL-Ebene sind in der os_sync.h.html die Aufrufe os_lockMutex(...) und os_unlockMutex(...) vorgesehen. Dazu ist ein Handle vom Typ OS_Mutex notwendig, das wie ein OS_HandleThread eine Zeiger auf eine nicht näher definierte struct ist, hinter der sich auch ein einfaches int als Handle aus dem Betriebssystem verbergen kann. Die notwendigen Daten für ein OS_Mutex sind gegebenenfalls bzw. meist betriebssystemintern. Um ein solches Handle zu bekommen, ist ein Aufruf von os_createMutex(name, dst) notwendig. Der übergebene Name dient zu Debugzwecken mit dem RTOS.


3.4 Tread-Synchronisierung

Topic:LibJc.Threads.OS_Interface.OS_WaitNotify


Topic:LibJc.Threads.OS_Interface.OS_WaitNotify.generally

Als Synchronisierung wird oft das gegenseitige Warten von Threads wegen Mutex bezeichnet, auch in Java ist das mit dem Schlüsselwort synchronize gekennzeichnet. Synchronisierung im normalem Sprachgebrauch ist aber weiterführend: Mehrere Threads müssen irgendwie geartet gleichlaufend Daten und Ereignisse behandeln. Implementierungstechnisch ist das mit einem Warten (wait) und Anzeigen (notify) verbunden, was denn auch die beiden Schlüsselworte dafür in Java sind.


Topic:LibJc.Threads.OS_Interface.OS_WaitNotify.os_wait

Auf OSAL-Ebene sind in der os_sync.h.html die Aufrufe os_wait(...) und os_notify(...) vorgesehen. Dazu ist ein Handle vom Typ OS_HandleWaitNotify notwendig, das wie ein OS_HandleThread eine Zeiger auf eine nicht näher definierte struct ist, hinter der sich auch ein einfaches int als Handle aus dem Betriebssystem verbergen kann. Die notwendigen Daten für ein OS_HandleWaitNotify sind gegebenenfalls bzw. meist betriebssystemintern. Um ein solches Handle zu bekommen, ist ein Aufruf von os_createWaitNotifyObject(name, dst) notwendig. Der übergebene Name dient zu Debugzwecken mit dem RTOS.


Topic:LibJc.Threads.OS_Interface.OS_WaitNotify.os_wait_Mutex

Ein wait(...) und notify(...) funktioniert nur zusammen mit einem Mutex. Das liegt daran, dass Daten, die mit dem wait(...) und notify(...) erwartet und übergeben werden, unter Mutex gelesen und gespeichert werden müssen. Ansonsten kann es beispielsweise vorkommen, dass ein wait(...) wegen nicht vorhandener Daten ausgelöst wurde, während dessen zufällig ein Threadwechsel stattfindet, mit dem die Daten eintreffen. Da die Daten in dem Moment aber noch keiner erwartet, wird kein notify(...) gerufen, es ist nicht bekannt wer notifiziert werden soll. Danach erfolgt die Weiterarbeit im unterbrochenem Thread, der zum warten führt, und zwar unendlich lange, obwohl oder gerade weil die Daten schon da sind. Diese Aufrufe mit einem Mutex zu verbinden, der vor dem wait(...) oder notify(...) gesetzt wird, von der internen Abarbeitung im wait(...) bzw notify(...) gelöst wird, bevor der Thread schlafengelegt wird bzw. notifiziert und beim Austritt intern wieder gesetzt wird, da vom Anwender danach gelöst, ist der allgemeine immer funktionstüchtige Ansatz. Eine Programmierung ohne diesen Mechanismus, wie er mit einer einfachen Semaphorenbehandlung möglich ist, ist nur dann sicher, wenn bekannt ist welcher Thread wartet und wenn es garantiert ein sauberes Wechselspiel von wait und notify gibt. Nur dann kann man eine Semaphore bei notify setzen auch wenn die zugehörige Semaphorenabfrage noch nicht geschehen ist, um die Tatsache zu nutzen dass ein Thread bei der Semaphorenabfrage nicht stehenbleibt wenn diese gesetzt ist. Wenn es kein garantiertes sauberes Wechselspiel gibt, es also ein quasi nicht vorgesehenes notify geben könnte, wenn ein anderer Thread noch gar nicht im Abfragezustand ist, dann wird bei der nächsten Abfrage der Thread nicht warten, weil alte Daten vorliegen. Auf diese Problematik sei an dieser Stelle deshalb so deutlich hingewiesen, weil das wait/notify-Schema oft mit der einfachen Semaphorenabfrage gelöst wird, wenn manuell in C programmiert wird. Diese einfache Semaphorenabfrage ist daher in der CRuntimeJavalike oder JcLib und in der zugehörigen OSAL-Schnittstellendefinition nicht vorgesehen, weil sie zu unischerer Programmierung verleitet.


3.5 Fehlerbehandlungsstrategien bei RTOS-Aufrufen

Topic:LibJc.Threads.OS_Interface.os_ErrorHandling


Topic:LibJc.Threads.OS_Interface.os_ErrorHandling.generally

In der Regel sind die Aufrufe von RTOS-Routinen so gebaut, dass bei Nichtausführbarkeit ein Fehler-Code zurückgegeben wird. Nur in extremen Fällen wird ein RTOS selbst eine Fehlermeldung absetzen und die Weiterarbeit verweigern. Fehler können beispielsweise dann auftreten, wenn die Anzahl der Threads erschöpft ist oder der übergebene Name bereits vorhanden ist. Das aufrufende Programm, muss immer den Fehlercode auswerten und aus RTOS-Sicht irgendwie geeignet reagieren.

Für die OSAL-Schnittstelle nach außen (zur CRuntimeJavalike, zum User) gelten folgende Regeln:


Topic:LibJc.Threads.OS_Interface.os_ErrorHandling.throwException

Das Exceptionhandling wie es in Exception_Jc.html beschrieben ist kann sowohl in der OSAL-Schicht als auch bereits aus der RTOS-Schicht heraus und dort auch in Ausnaheminterrupt-Behandlungen gerufen werden. Da das Exceptionhandling in diesem Layer aber unbekannt ist sondern zu einem darüberliegenden Layer (Fwc) gehört, erfolgt kein direkter Aufruf. Stattdessen ist eine Schnittstelle als Routine os_Error(text, value1, value2) in OSAL/src/os_internal_common.h definiert. In dieser Routine erfolgt ein callback (über Funktionspointer) auf die Routine, die mit os_setErrorRoutine(...) (OSAL/inc/error.h) übergeben wurde. Von der JcLib wird dort eine Routine eingetragen, die ein THROW erzeugt, das im Exceptionhandling abgefangen wird. Dieser Umweg ist deshalb notwendig, weil sonst eine Verletzung der Layermodell-Regel stattfindet, nach der nie eine Routinen eines oberen Layers von unten gerufen werden darf. Diese Verletzung würde sich darin äußern, dass die OSAL-Schicht mit dem RTOS nicht mehr eigenständig link- und testbar ist.


Topic:LibJc.Threads.OS_Interface.os_ErrorHandling.RTOS_ExceptionInterrupt

Wenn aufgrund einer Ausnahmesituation von der CPU ein Ausnahme-Interrupt gerufen wird, beispielsweise wegen Division durch 0 oder null-Pointer, dann wird diese reine Interrupt-Routine den Stack des laufenden Threads benutzen. Der Interrupt muss quittiert werden. Wenn danach der Aufruf von os_error(text, value1, value2) programmiert ist, dann wird nichts ausgeführt wenn keine User-(Exception-) -Routine gesetzt ist. Da aber ein zu behandelnter Fehler vorliegt, ist meist vorgesehen, dass das System dann in den Stop geht. System-Halt wegen Division durch 0 oder null-Pointer. Kennt sicherlich jeder. Eine andere Möglichkeit wäre eine Notation der Fehlersituation (irgendeine betriebssystemnahe Queue) und beenden des Interrupts. In diesem Fall läuft der Thread weiter mit einem fehlerhaftem Wert. Entweder Folgefehler werden erzeugt, oder das Programm hat falsche Daten. Alle diese Varianten sind eigentlich nicht tragbar. Situationen der Ausnahme, die zu einem CPU-Interrupt führen, kommen aber in der Praxis während der Programmentwicklung häufig vor.

Wenn die os_error(...)-Routine aber vom Exceptionhandling gesetzt ist, dann gilt folgendes:

Diese Behandlung erfordert, dass die CPU-Exceptioninterrupts entsprechend programmiert werden.


4 Startegie der Fehlerbehandlung

Topic:LibJc.Threads.ErrorHandling

Die Strategie der CRuntimeJavalike oder JcLib ist das Exceptionhandling. Dieses ist auf diesem Layer bereits definiert und soll daher auch genutzt werden. Damit führen Fehler-Rückgabewerte in den OSAL-Threadroutinen in der CRuntimeJavalike zu einer Exception, wie es auch in Java ist. Der Vorteil ist: Der Anwender braucht sich nicht um die Fehlerbehandlung zu kümmern. Es sind meist nicht erwartete Fehler, die also auch kaum abgefangen werden müssen. Ein weiterer Vorteil ist: Der Anwender kann eine Auswertung eines Rückgabecodes nicht vergessen, da es diesen gar nicht gibt. Damit werden Fehler in jedem Falle erkannt und erzeugen keine Folgefehler.


5 Implementierung ThreadJc

Topic:LibJc.Threads.ThreadJc


Topic:LibJc.Threads.ThreadJc.content

Die Klasse docLibJc:ThreadJc enthält nur einige wenige Werte, die den Thread äußerlich beschreiben. Die wichtigsten Daten sind:

Eine Instanz von ThreadJc kann die Basis der Aktiven Klasse bilden, oder auch nur eine relativ kleine Verwaltungsinstanz des Threads sein, je nachdem ob die Aktive Klasse auf ThreadJc basiert oder stattdessen 'nur' das Interface RunnableJc implementiert. Das sind dieselben beiden Wahlmöglichkeiten wie in Java. Die zweite Möglichkeit ist dann notwendig, wenn die Aktive Klasse eine andere Klasse erweitert. Der Begriff Aktive Klasse ist wie in der UML gemeint: Instanzen von Aktive Klassen bilden einen eignenen Thread. Die aktive Klasse enthält die Methode run(), die in RunnableJc definiert ist. Diese Methode ist die main-Methode des aktivierten Threads. Solange diese Methode abgearbeitet wird, existiert der Thread. Die Auswahl, ob ThreadJc die Basis der aktiven Klasse ist oder nur ein Helfer, wird mit dem Konstruktor geklärt:

Der Aufruf von start_ThreadJc() testet dann, ob target.run() als Thread-Routine angegeben wird oder die eigene run(). Diese Methode ruft dann os_createThread(...) auf.


Topic:LibJc.Threads.ThreadJc.stackSize

In Java wird keine StackSize für einen Thread angegeben. Die Java-VM verwaltet dies selbst. In der VM ist es grundsätzlich auch möglich, den Stack eines Threads umzulagern und dabei zu vergrößern, wenn eine Grenze erreicht ist.

Letzteres ist bei schnellen Echtzeitsystemen nicht möglich, da eine Umorganisation Zeit benötigt. Die Wahl der Stackgröße ist eine Aufgabe, bei der nach Erfahrungen abgeschätzt werden muss, wieviel notwendig ist. Dieser Wert hängt von verschiedenen Variationen des Programmlaufes ab und hat im Nichtfehlerfall ein Maximum, das aber leider nirgends direkt ablesbar ist.

Für schnelle Echtzeitverarbeitung ist die Vorgabe einer Stackgröße notwendig. Man kann sich mit einem Defaultwert aus der Unkenntnis retten, oder einen recht hohen Wert angegeben, um auf der sicheren Seite zu sein. Es kommt darauf an, ob Speicher dafür knapp ist.

Entsprechend den Darstellungen in $chapter ist es möglich, im Stack auch große Instanzen anzulegen. Deren Gesamtgröße ist selbstverständlich bei der Kalkulation der Stackgröße zu berücksichtigen. Man wird dann meist nicht mehr mit dem Defaultwert hinkommen.

Die Stackgröße wird immer mit der Methode start_ThreadJc angegeben. Der Defaultwert gilt, wenn 0 oder kleiner 0 (-1) parametriert wird. Da die Angabe in einem Java-Programm grundsätzlich nicht erfolgt, wird dies als Sonderfall einer Java2C-Translation berücksichtigt. Man kann bei Java2C die Stacksize als Annotation @java2c=stackSize(value). angegeben. Ohne diese Angabe wird dann auch dort der Defaultwert benutzt.


Topic:LibJc.Threads.ThreadJc.dynCall

Der Aufruf der richtigen, überladenen Routine für run() geschieht mit den Mitteln des dynamischen Bindens, wie es in dynCall.html dargestellt ist.


6 Implementierung synchronized (Mutex)

Topic:LibJc.Threads.synchronized


Topic:LibJc.Threads.synchronized.idSyncHandles

Jedes Object hat die Möglichkeit der Nutzung eines Mutex. Die Idee, eine Mutex-handle nicht extra zu führen sondern an die Basisklasse Object zu binden, ist eine Java-Idee für eine semantisch sichere Programmierung. Es besteht zwar immer noch die Verwechslungsmöglichkeit wie

 synchronized(object1) {
   objectOther->access();
 }

aber solche Fehler fallen bei einem formellen Review bzw. beim normalen Durchschauen der Programmierung auf.

In einem ObjectJc sind nun für diesen Fall nur 2 Bytes spendiert, für einen 16-bit-Index. Damit ist die Tatsache beachtet worden,dass die meisten Instanzen nicht für synchronized benutzt werden. Der Index steht zunächst auf -1. Erst wenn die Routine synchronized_ObjectJc(instance) gerufen wird, dann wird ein Mutex im RTOS angelegt. Dessen Handle wird in einer Tabelle data_OsWrapperJc.handleItemsJc[...] gespeichert. Der Index auf dieses Array wird dann in ObjectJc::idSyncHandles vermerkt. Die Größe dieser Tabelle ist fest und auf den Anwendungsfall zuzuschneiden. Allerdings kann man hier reichlich bemessen, da pro Eintrag 8 Byte benötigt werden und die Anzahl der notwendigen Mutex-Handle eher meist nur zweistellig ist.

Nach dem ersten Aufruf von synchronized ist das Handle für den Mutex des RTOS fest mit dem Object verbunden und bleibt solange erhalten, bis das Object gelöscht wird.


Topic:LibJc.Threads.synchronized.synchroizedViaMPU

Es gibt eine andere zweckmäßige Lösung, die für Anwendungen der sicherheitsrelevanten Programmierung wichtig sein könnte: Ein Object wird in einem Speicherbereich angelegt, der vom unterlagertem RTOS verwaltet wird. Ein Zugriff darauf wird mit einer MPU (Memory Protection Unit) als Teil der CPU geschützt. Der Zugriff ist nur möglich, wenn synchronized_ObjectJc(object) gerufen wird. In diesem Fall wird vom RTOS geprüft, ob das Object frei ist, und dessen Adressbereich wird in der MPU dann vom RTOS freigeschaltet bis endSynchronized... gerufen wird. Es kann dann in dieser Zeit kein anderer Thread auf diesem Object arbeiten, insbesondere nicht mit unsynchronisierten Zugriffen. Damit werden Programmierfehler des unsynchronisierten Zugriffes zur Laufzeit entdeckt und sicher vermieden.


Topic:LibJc.Threads.synchronized.time_nested

Die Nutzungs-Zeit eines synchronized-Zugriffes auf ein Object kann in der Regel jeweils sehr kurz sein, es kann sich aber auch um eine längere Zugriffsrechtvergabe handeln, beispielsweise für Berechnungen jeweils mit Warten auf Außenkommunikation. Alle anderen Threads, die auf dieses Object zugreifen, werden dann angehalten. Wenn aber ein anderweitiger Zugriff nicht stattfindet, dann ist der längere Zugriff statthaft. Ähnlich ist es mit geschachtelten synchronized jeweils auf verschiedene Objects. Diese sind dann statthaft wenn funktional gerechtfertigt und wenn eine Blockade ausgeschlossen ist. Ansonsten kann so ein geschachteltes synchronized allerdings sehr schnell zu einem Deadlock führen. Das geschachtelte Aufrufen eines synchronized auf das selbe Object vom selben Thread ist dagegen immer möglich. Diese Dinge sind in Java genauso gültig wie in der CRuntimeJavalike oder JcLib.


7 Implementierung wait und notify

Topic:LibJc.Threads.waitNotify


Topic:LibJc.Threads.waitNotify.idSyncHandles

Für wait und notify ist ein OS_HandleWaitNotify des RTOS notwendig. Dieses wird zusammen mit dem OS_Mutex-handle in derselben Tabelle niederlegt und gemeinsam mit dem ObjectJc::idSyncHandles-Element indiziert. Das Handle wird im RTOS angelegt beim ersten Aufruf von wait oder notify.


8 Dokumentation generiert aus Headerfile

UML=/Package[ThreadJc_h]/Namespace.ownedElement[]/Class[RunnableJc_s]

Class RunnableJc_s im Package ThreadJc_h

UML=/Package[ThreadJc_h]/Namespace.ownedElement[]/Class[RunnableJc_s]

Attributes:

+-------'-------'-------'-------'-------'-------'-------'-------+
|                             base                              |
+-------'-------'-------'-------'-------'-------'-------'-------
Pos. 0x0..0x = 0..NaN ( NaN bytes): base :

Inner class RunnableJc_s.Mtbl_ definiert in ThreadJc_h/RunnableJc_s

UML=/Class[RunnableJc_s]/Namespace.ownedElement[]/Class[Mtbl_]

Attributes:

+-------'-------'-------'-------'-------'-------'-------'-------+
|                             head                              |
+-------'-------'-------'-------+-------'-------'-------'--...8+
|             run               |           ObjectJc            |
+-------'-------'-------'-------'-------'-------'-------'------
Pos. 0x0..0x4 = 0..4 ( 5 bytes): head : MtblHeadJc
Pos. 0x5..0x8 = 5..8 ( 4 bytes): run : MT_run_RunnableJc
Pos. 0x9..0x21 = 9..33 ( 25 bytes): ObjectJc :

Methoden:

run_RunnableJc (ithis, _thCxt) : void

Call of the method at this class level, executes a dynamic call of the override-able method:

  • returns: void -


UML=/Package[ThreadJc_h]/Namespace.ownedElement[]/Class[ThreadJc_s]

Class ThreadJc_s im Package ThreadJc_h

UML=/Package[ThreadJc_h]/Namespace.ownedElement[]/Class[ThreadJc_s]

Attributes:

+-------'-------'-------'-------'-------'-------'-------'-------+
|                             base                              |
+-------'-------'-------'-------'-------'-------'-------'-------+
|                           hThread                             |
+-------'-------'-------'-------'-------'-------'-------'-------+
|                             name                              |
+-------'-------'-------'-------'-------'-------'-------'-------+
|                            target                             |
+-------'-------'-------'-------+-------'-------'-------'-------+
|          stackSize            |            state              |
+-------'-------'-------'-------+-------+
|          nPriority            |sCalledName|
+-------'-------'-------'-------'-------
Pos. 0x0..0x = 0..NaN ( NaN bytes): base :
Pos. 0x..0x = NaN..NaN ( NaN bytes): hThread : OS_HandleThread

Ident of the thread, to check the correctnis (debugmodi).

Pos. 0x..0x = NaN..NaN ( NaN bytes): name : StringJc

Name of the thread.

Pos. 0x..0x = NaN..NaN ( NaN bytes): target : RunnableJcREF

The pointer to the associated Runnable Object, contains the run()-method. This pointer will be set only in the constructor, if an associated Runnable-object is given. The pointer may be null, if the run-method of this class is overridden.

Pos. 0x..0x = NaN..NaN ( 4 bytes): stackSize : int32

The given stackSize.

Pos. 0x..0x = NaN..NaN ( 4 bytes): state : int32

State of the thread.

Pos. 0x..0x = NaN..NaN ( 4 bytes): nPriority : int32

The priority of thread is able to adjust.

Pos. 0x..0x = NaN..NaN ( 1 bytes): sCalledName : char

pointer to the Stack with name of the actual called method saved on Thread switch

Inner class ThreadJc_s.Mtbl_ definiert in ThreadJc_h/ThreadJc_s

UML=/Class[ThreadJc_s]/Namespace.ownedElement[]/Class[Mtbl_]

Attributes:

+-------'-------'-------'-------'-------'-------'-------'-------+
|                             head                              |
+-------'-------'-------'-------'-------'-------'-------'-------+
|                           ObjectJc                            |
+-------'-------'-------'-------'-------'-------'-------'-------+
|                          RunnableJc                           |
+-------'-------'-------'-------'-------'-------'-------'-------
Pos. 0x0..0x4 = 0..4 ( 5 bytes): head : MtblHeadJc
Pos. 0x5..0x1D = 5..29 ( 25 bytes): ObjectJc :
Pos. 0x1E..0x3F = 30..63 ( 34 bytes): RunnableJc : Mtbl_

Methoden:

getId_ThreadJc (THIZ) :
  • returns: -

finalize_ThreadJc_F (othis, _thCxt) : void

Finalize declaration. It is called by Garbage collector and inside other finalized methods. It should be called by the user if the instance is removed.

  • returns: void -

ctorO_ThreadJc (OTHIS, THCXT) :
  • OTHIS: -

  • THCXT: -

  • returns: -

ctorO_z_ThreadJc (OTHIS, NAME, THCXT) :
  • OTHIS: -

  • NAME: -

  • THCXT: -

  • returns: -

ctorO_s_ThreadJc (OTHIS, NAME, THCXT) :
  • OTHIS: -

  • NAME: -

  • THCXT: -

  • returns: -

ctorO_Runnable_z_ThreadJc (OTHIS, RUN, NAME, THCXT) :
  • OTHIS: -

  • RUN: -

  • NAME: -

  • THCXT: -

  • returns: -

ctorO_Runnable_s_ThreadJc (othis, pRunnable, pName, _thCxt) : ThreadJc_s
getName_ThreadJc (THIZ, THCXT) :
  • THCXT: -

  • returns: -

setPriority_ThreadJc (ythis, newPriority) : void
  • newPriority: int -

  • returns: void -

getPriority_ThreadJc (ythis) : int
  • returns: int -

start_ThreadJc (ythis, stackSize, _thCxt) : void
  • stackSize: int -

  • returns: void -

run_Thread_F (ithis, _thCxt) : void

The original virtual method run of class Thread, used for an immediate non-dynamic call.

  • returns: void -

run_ThreadJc (ithis, _thCxt) : void

The call of run, executes a dynamic call of the override-able method.

  • returns: void -

isAlive_ThreadJc () : bool
  • Tests if this thread is alive. A thread is alive if it has

  • been started and has not yet died.

  • ': ThreadJc_s -

  • returns: bool - <code>true</code> if this thread is alive;

    • <code>false</code> otherwise.

finalize_ThreadJc () : void
sleep_ThreadJc (milliseconds, _thCxt) : void
  • milliseconds: int32 -

  • returns: void -

currentThread_ThreadJc () : ThreadJc_s

javalike:

CONST-Initializer:

CONST-Initializer sind Makros für C-Konstanten, sie sind in Headerfiles definiert. C-Konstante sind Konstrukte in { ... } mit ausschließlich konstanten Werten. Die Makros der CONST-Initializer übernehmen Argumente, die zur Compilezeit die Konstanten bestimmen. Die Initializer sind deshalb als Makros definiert, weil die Struktur der Konstanten exakt der struct-Definition folgen muss, die struct ist im Headerfile definiert, daher auch dazu passend diese Makros. Mit denCONST-Intializer ist es auf einfache Weise möglich, C-struct-Daten (Plain Old Data) zu initialisieren. Für Klassen (C++) ist dieses Konzept nicht geeignet.

CONST_Runnable_ThreadJc(RUNNABLE)
  • RUNNABLE -

CONST_Runnable_z_ThreadJc(RUNNABLE, NAME)

Constant in form {...} defines a Thread with associated Runnable class

  • RUNNABLE -

  • NAME -