diff -u -r --exclude=CVS xfer/tcljava/src/native/javaNotifier.c web/tcljava/src/native/javaNotifier.c --- xfer/tcljava/src/native/javaNotifier.c Thu Oct 26 12:10:03 2000 +++ web/tcljava/src/native/javaNotifier.c Thu Oct 26 12:25:58 2000 @@ -8,18 +8,26 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: javaNotifier.c,v 1.2.2.3 2000/08/27 05:09:00 mo Exp $ + * RCS: @(#) $Id: javaNotifier.c,v 1.3 2000/10/02 23:11:25 danw Exp $ */ #include "java.h" #include "javaNative.h" -/* - * Global Notifier object. - */ +static Tcl_ThreadDataKey dataKey; + +typedef struct { + + jobject notifierObj; + + Tcl_ThreadId tclNotifierTid; + + int eventQueued; -static jobject globalNotifierObj; -static int eventQueued = 0; +} ThreadSpecificData; + +/* Define this here so that we do not need to include tclInt.h */ +#define TCL_TSD_INIT(keyPtr) (ThreadSpecificData *)Tcl_GetThreadData((keyPtr), sizeof(ThreadSpecificData)) /* * Declarations for functions used only in this file. @@ -45,11 +53,18 @@ *---------------------------------------------------------------------- */ -void JNICALL +jlong JNICALL Java_tcl_lang_Notifier_init( JNIEnv *env, /* Java environment. */ jobject notifierObj) /* Handle to Notifier object. */ { + ThreadSpecificData *tsdPtr; + + tsdPtr = TCL_TSD_INIT(&dataKey); + + tsdPtr->notifierObj = (*env)->NewGlobalRef(env, notifierObj); + tsdPtr->eventQueued = 0; + tsdPtr->tclNotifierTid = Tcl_GetCurrentThread(); /* If we segfault near here under Windows, try removing tclblend.dll * from the current directory. Tcl Blend has problems loading @@ -57,8 +72,8 @@ * same name in the local directory. */ Tcl_CreateEventSource(NotifierSetup, NotifierCheck, NULL); - JavaInitNotifier(); - globalNotifierObj = (*env)->NewGlobalRef(env, notifierObj); + + return (jlong)tsdPtr->tclNotifierTid; } /* @@ -82,10 +97,12 @@ JNIEnv *env, /* Java environment. */ jobject notifierObj) /* Handle to Notifier object. */ { + ThreadSpecificData *tsdPtr; + Tcl_DeleteEventSource(NotifierSetup, NotifierCheck, NULL); - JavaDisposeNotifier(); - (*env)->DeleteGlobalRef(env, globalNotifierObj); - globalNotifierObj = NULL; + tsdPtr = TCL_TSD_INIT(&dataKey); + (*env)->DeleteGlobalRef(env, tsdPtr->notifierObj); + tsdPtr->notifierObj = NULL; } /* @@ -139,14 +156,11 @@ void JNICALL Java_tcl_lang_Notifier_alertNotifier( JNIEnv *env, /* Java environment. */ - jobject notifierObj) /* Handle to Notifier object. */ + jobject notifierObj, /* Handle to Notifier object. */ + jlong tid) /* Tcl_ThreadId for the notifier thread */ { - /* - * This interface is intended to be called from other threads, - * so we should not grab the monitor. - */ - JavaAlertNotifier(); + Tcl_ThreadAlert((Tcl_ThreadId)tid); } /* @@ -173,8 +187,11 @@ { JNIEnv *env = JavaGetEnv(); JavaInfo* jcache = JavaGetCache(); + ThreadSpecificData *tsdPtr; + + tsdPtr = TCL_TSD_INIT(&dataKey); - if ((*env)->CallBooleanMethod(env, globalNotifierObj, jcache->hasEvents) + if ((*env)->CallBooleanMethod(env, tsdPtr->notifierObj, jcache->hasEvents) == JNI_TRUE) { Tcl_Time timeout = { 0, 0 }; Tcl_SetMaxBlockTime(&timeout); @@ -206,18 +223,21 @@ Tcl_Event *ePtr; JNIEnv *env = JavaGetEnv(); JavaInfo* jcache = JavaGetCache(); + ThreadSpecificData *tsdPtr; + tsdPtr = TCL_TSD_INIT(&dataKey); /* * Only queue a new event if there isn't already one queued and * there are events on the Java event queue. */ - if (!eventQueued && (*env)->CallBooleanMethod(env, globalNotifierObj, - jcache->hasEvents) == JNI_TRUE) { + if (!tsdPtr->eventQueued && + (*env)->CallBooleanMethod(env, tsdPtr->notifierObj, + jcache->hasEvents) == JNI_TRUE) { ePtr = (Tcl_Event *) ckalloc(sizeof(Tcl_Event)); ePtr->proc = JavaEventProc; Tcl_QueueEvent(ePtr, TCL_QUEUE_TAIL); - eventQueued = 1; + tsdPtr->eventQueued = 1; } } @@ -245,21 +265,19 @@ { JNIEnv *env = JavaGetEnv(); JavaInfo* jcache = JavaGetCache(); - + ThreadSpecificData *tsdPtr; + + tsdPtr = TCL_TSD_INIT(&dataKey); + /* * Call Notifier.serviceEvent() to handle invoking the next event and * signaling any threads that are waiting on the event. */ - eventQueued = 0; - - /* - * It is safe to leave the monitor here since we assume nothing about the - * state of the world after we return. - */ + tsdPtr->eventQueued = 0; - (void) (*env)->CallIntMethod(env, globalNotifierObj, jcache->serviceEvent, - flags); + (void) (*env)->CallIntMethod(env, tsdPtr->notifierObj, + jcache->serviceEvent, flags); return 1; } diff -u -r --exclude=CVS xfer/tcljava/src/tclblend/tcl/lang/Notifier.java web/tcljava/src/tclblend/tcl/lang/Notifier.java --- xfer/tcljava/src/tclblend/tcl/lang/Notifier.java Thu Oct 26 12:10:03 2000 +++ web/tcljava/src/tclblend/tcl/lang/Notifier.java Thu Oct 26 12:23:29 2000 @@ -9,13 +9,14 @@ * redistribution of this file, and for a DISCLAIMER OF ALL * WARRANTIES. * - * RCS: @(#) $Id: Notifier.java,v 1.2.4.1 2000/08/24 06:56:11 mo Exp $ + * RCS: @(#) $Id: Notifier.java,v 1.3 2000/05/13 10:17:11 mo Exp $ * */ package tcl.lang; -import java.util.*; +import java.util.Hashtable; +import java.util.Vector; /* * Implements the Blend version of the Notifier class. The Notifier is @@ -24,7 +25,7 @@ * events. The Notifier manages an event queue that holds TclEvent * objects. * - * The Notifier is designed to run in a multi-threaded + * The Blend notifier is designed to run in a multi-threaded * environment. Each notifier instance is associated with a primary * thread. Any thread can queue (or dequeue) events using the * queueEvent (or deleteEvents) call. However, only the primary thread @@ -41,27 +42,80 @@ public class Notifier implements EventDeleter { -private static Notifier globalNotifier; - -// First pending event, or null if none. +/* + * First pending event, or null if none. + */ private TclEvent firstEvent; -// Last pending event, or null if none. +/* + * Last pending event, or null if none. + */ private TclEvent lastEvent; -// Last high-priority event in queue, or null if none. +/* + * Last high-priority event in queue, or null if none. + */ private TclEvent markerEvent; -// The primary thread of this notifier. Only this thread should process -// events from the event queue. +/* + * The primary thread of this notifier. Only this thread should process + * events from the event queue. + */ Thread primaryThread; -// Reference count of the notifier. It's used to tell when a notifier -// is no longer needed. +/* + * Stores the Notifier for each thread. + */ + +private static Hashtable notifierTable = new Hashtable(); + +/* + * tcl ThreadId + */ + +private long tclThreadId; + + +/* + * List of registered timer handlers. + */ + + +Vector timerList; + +/* + * Used to distinguish older timer handlers from recently-created ones. + */ + +int timerGeneration; + +/* + * True if there is a pending timer event in the event queue, false + * otherwise. + */ + +boolean timerPending; + +/* + * List of registered idle handlers. + */ + +Vector idleList; + +/* + * Used to distinguish older idle handlers from recently-created ones. + */ + +int idleGeneration; + +/* + * Reference count of the notifier. It's used to tell when a notifier + * is no longer needed. + */ int refCount; @@ -89,12 +143,12 @@ Notifier( Thread primaryTh) // The primary thread for this Notifier. { - primaryThread = primaryTh; + primaryThread = primaryTh; firstEvent = null; lastEvent = null; markerEvent = null; - refCount = 0; - init(); + refCount = 0; + tclThreadId = init(); } /* @@ -103,8 +157,7 @@ * getNotifierForThread -- * * Get the notifier for this thread, creating the Notifier, - * when necessary. Note that this is a partial implementation - * that only supports a single notifier thread. + * when necessary. * * Results: * The Notifier for this thread. @@ -119,12 +172,13 @@ getNotifierForThread( Thread thread) // The thread that owns this Notifier. { - if (globalNotifier == null) { - globalNotifier = new Notifier(thread); - } else if (globalNotifier.primaryThread != thread) { - return null; - } - return globalNotifier; + Notifier notifier = (Notifier) notifierTable.get(thread); + if (notifier == null) { + notifier = new Notifier(thread); + notifierTable.put(thread, notifier); + } + + return notifier; } /* @@ -134,7 +188,7 @@ * * Increment the reference count of the notifier. The notifier will * be kept in the notifierTable (and alive) as long as its reference - * count is greater than zero. This method is concurrent safe. + * count is greater than zero. * * Results: * None. @@ -149,11 +203,10 @@ preserve() { synchronized (notifierMutex) { - if (refCount < 0) { - throw new TclRuntimeError( - "Attempting to preserve a freed Notifier"); - } - ++refCount; + if (refCount < 0) { + throw new TclRuntimeError("Attempting to preserve a freed Notifier"); + } + ++refCount; } } @@ -163,8 +216,7 @@ * release -- * * Decrement the reference count of the notifier. The notifier will - * be free when its refCount goes from one to zero. This method is - * concurrent safe. + * be free when its refCount goes from one to zero. * * Results: * None. @@ -180,19 +232,19 @@ release() { synchronized (notifierMutex) { - if ((refCount == 0) && (primaryThread != null)) { - throw new TclRuntimeError( - "Attempting to release a Notifier before it's preserved"); - } - if (refCount <= 0) { - throw new TclRuntimeError("Attempting to release a freed Notifier"); - } - --refCount; - if (refCount == 0) { - primaryThread = null; - globalNotifier = null; - dispose(); - } + if ((refCount == 0) && (primaryThread != null)) { + throw new TclRuntimeError( + "Attempting to release a Notifier before it's preserved"); + } + if (refCount <= 0) { + throw new TclRuntimeError("Attempting to release a freed Notifier"); + } + --refCount; + if (refCount == 0) { + notifierTable.remove(primaryThread); + primaryThread = null; + dispose(); + } } } @@ -206,8 +258,7 @@ * Events inserted before the marker will be processed in * first-in-first-out order, but before any events inserted at * the tail of the queue. Events inserted at the head of the - * queue will be processed in last-in-first-out order. This method is - * concurrent safe. + * queue will be processed in last-in-first-out order. * * Results: * None. @@ -224,51 +275,63 @@ queueEvent( TclEvent evt, // The event to put in the queue. int position) // One of TCL.QUEUE_TAIL, - // TCL.QUEUE_HEAD or TCL.QUEUE_MARK. + // TCL.QUEUE_HEAD and TCL.QUEUE_MARK. { evt.notifier = this; synchronized (notifierMutex) { - if (position == TCL.QUEUE_TAIL) { - // Append the event on the end of the queue. - evt.next = null; - - if (firstEvent == null) { - firstEvent = evt; - } else { - lastEvent.next = evt; - } - lastEvent = evt; - } else if (position == TCL.QUEUE_HEAD) { - // Push the event on the head of the queue. - evt.next = firstEvent; - if (firstEvent == null) { - lastEvent = evt; - } - firstEvent = evt; - } else if (position == TCL.QUEUE_MARK) { - // Insert the event after the current marker event and advance - // the marker to the new event. - if (markerEvent == null) { - evt.next = firstEvent; - firstEvent = evt; - } else { - evt.next = markerEvent.next; - markerEvent.next = evt; - } - markerEvent = evt; - if (evt.next == null) { - lastEvent = evt; - } - } else { - // Wrong flag. - throw new TclRuntimeError("wrong position \"" + position + - "\", must be TCL.QUEUE_HEAD, TCL.QUEUE_TAIL or TCL.QUEUE_MARK"); - } - } + if (position == TCL.QUEUE_TAIL) { + /* + * Append the event on the end of the queue. + */ + + evt.next = null; + if (firstEvent == null) { + firstEvent = evt; + } else { + lastEvent.next = evt; + } + lastEvent = evt; + } else if (position == TCL.QUEUE_HEAD) { + /* + * Push the event on the head of the queue. + */ + + evt.next = firstEvent; + if (firstEvent == null) { + lastEvent = evt; + } + firstEvent = evt; + } else if (position == TCL.QUEUE_MARK) { + /* + * Insert the event after the current marker event and advance + * the marker to the new event. + */ + + if (markerEvent == null) { + evt.next = firstEvent; + firstEvent = evt; + } else { + evt.next = markerEvent.next; + markerEvent.next = evt; + } + markerEvent = evt; + if (evt.next == null) { + lastEvent = evt; + } + } else { + /* + * Wrong flag. + */ + + throw new TclRuntimeError("wrong position \"" + position + + "\", must be TCL.QUEUE_HEAD, TCL.QUEUE_TAIL or TCL.QUEUE_MARK"); + } + } if (Thread.currentThread() != primaryThread) { - alertNotifier(); + //notifyAll(); + alertNotifier(); } } @@ -279,8 +342,7 @@ * * Calls an EventDeleter for each event in the queue and deletes * those for which deleter.deleteEvent() returns 1. Events - * for which the deleter returns 0 are left in the queue. This - * method is concurrent safe. + * for which the deleter returns 0 are left in the queue. * * Results: * None. @@ -299,31 +361,32 @@ TclEvent evt, prev; synchronized (notifierMutex) { - for (prev = null, evt = firstEvent; evt != null; evt = evt.next) { - if (deleter.deleteEvent(evt) == 1) { - if (firstEvent == evt) { - firstEvent = evt.next; - if (evt.next == null) { - lastEvent = null; - } + for (prev = null, evt = firstEvent; evt != null; evt = evt.next) { + if (deleter.deleteEvent(evt) == 1) { + if (firstEvent == evt) { + firstEvent = evt.next; + if (evt.next == null) { + lastEvent = null; + } if (markerEvent == evt) { markerEvent = null; } + } else { + prev.next = evt.next; + if (evt.next == null) { + lastEvent = prev; + } + if (markerEvent == evt) { + markerEvent = prev; + } + } } else { - prev.next = evt.next; - if (evt.next == null) { - lastEvent = prev; + prev = evt; } - if (markerEvent == evt) { - markerEvent = prev; - } - } - } else { - prev = evt; } } - } } + /* *---------------------------------------------------------------------- @@ -350,14 +413,14 @@ return ((evt.isProcessing == true) && (evt.isProcessed == true)) ? 1 : 0; } + /* *---------------------------------------------------------------------- * * serviceEvent -- * - * Process one event from the event queue. This method is concurrent - * safe and can be reentered recursively. + * Process one event from the event queue. * * Results: * The return value is 1 if the procedure actually found an event @@ -381,12 +444,15 @@ { TclEvent evt, prev; - // No event flags is equivalent to TCL_ALL_EVENTS. + /* + * No event flags is equivalent to TCL_ALL_EVENTS. + */ if ((flags & TCL.ALL_EVENTS) == 0) { flags |= TCL.ALL_EVENTS; } + // Loop through all the events in the queue until we find one // that can actually be handled. @@ -406,26 +472,24 @@ // can't depend on pointers found now still being valid when // the handler returns. synchronized (evt) { - boolean b = evt.isProcessing; - evt.isProcessing = true; + boolean b = evt.isProcessing; + evt.isProcessing = true; - if ((b == false) && (evt.processEvent(flags) != 0)) { - evt.isProcessed = true; - if (evt.needsNotify) { - evt.notifyAll(); + if ((b == false) && (evt.processEvent(flags) != 0)) { + evt.isProcessed = true; + if (evt.needsNotify) { + evt.notifyAll(); } - - deleteEvents(this); // this takes care of deletion of the - // just processed event - return 1; - } else { - // The event wasn't actually handled, so we have to - // restore the isProcessing field to allow the event to be - // attempted again. + deleteEvents(this); + return 1; + } else { + // The event wasn't actually handled, so we have to + // restore the isProcessing field to allow the event to be + // attempted again. - evt.isProcessing = b; + evt.isProcessing = b; - // The handler for this event asked to defer it. Just go on to + // The handler for this event asked to defer it. Just go on to // the next event. we will try to find another event to // process when the while loop continues } @@ -434,7 +498,7 @@ return 0; } - + /* *---------------------------------------------------------------------- * @@ -482,9 +546,7 @@ * Process a single event of some sort. If there's no work to * do, wait for an event to occur, then process it. May delay * execution of process while waiting for an event, unless - * TCL.DONT_WAIT is set in the flags argument. This routine - * should only be called from the primary thread for the - * notifier. + * TCL.DONT_WAIT is set in the flags argument. * * Results: * The return value is 1 if the procedure actually found an event @@ -501,7 +563,6 @@ * *---------------------------------------------------------------------- */ - public native int doOneEvent( int flags); // Miscellaneous flag values: may be any @@ -509,6 +570,7 @@ // TCL.WINDOW_EVENTS, TCL.FILE_EVENTS, // TCL.TIMER_EVENTS, TCL.IDLE_EVENTS, // or others defined by event sources. + /* *---------------------------------------------------------------------- @@ -526,8 +588,14 @@ *---------------------------------------------------------------------- */ -private final native void -alertNotifier(); +private final void +alertNotifier() +{ + alertNotifier(tclThreadId); +} + +private native void +alertNotifier(long tid); /* *---------------------------------------------------------------------- @@ -545,8 +613,9 @@ *---------------------------------------------------------------------- */ -private final native void +private final native long init(); + /* *---------------------------------------------------------------------- @@ -590,6 +659,11 @@ boolean result = (getAvailableEvent(null) != null); return result; -} +} + } // end Notifier + + + +