OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tcl/] [unix/] [tclUnixNotfy.c] - Blame information for rev 578

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
2
 * tclUnixNotify.c --
3
 *
4
 *      This file contains the implementation of the select-based
5
 *      Unix-specific notifier, which is the lowest-level part of the
6
 *      Tcl event loop.  This file works together with
7
 *      ../generic/tclNotify.c.
8
 *
9
 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
10
 *
11
 * See the file "license.terms" for information on usage and redistribution
12
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13
 *
14
 * RCS: @(#) $Id: tclUnixNotfy.c,v 1.1.1.1 2002-01-16 10:25:37 markom Exp $
15
 */
16
 
17
#include "tclInt.h"
18
#include "tclPort.h"
19
#include <signal.h> 
20
 
21
/*
22
 * This structure is used to keep track of the notifier info for a
23
 * a registered file.
24
 */
25
 
26
typedef struct FileHandler {
27
    int fd;
28
    int mask;                   /* Mask of desired events: TCL_READABLE,
29
                                 * etc. */
30
    int readyMask;              /* Mask of events that have been seen since the
31
                                 * last time file handlers were invoked for
32
                                 * this file. */
33
    Tcl_FileProc *proc;         /* Procedure to call, in the style of
34
                                 * Tcl_CreateFileHandler. */
35
    ClientData clientData;      /* Argument to pass to proc. */
36
    struct FileHandler *nextPtr;/* Next in list of all files we care about. */
37
} FileHandler;
38
 
39
/*
40
 * The following structure is what is added to the Tcl event queue when
41
 * file handlers are ready to fire.
42
 */
43
 
44
typedef struct FileHandlerEvent {
45
    Tcl_Event header;           /* Information that is standard for
46
                                 * all events. */
47
    int fd;                     /* File descriptor that is ready.  Used
48
                                 * to find the FileHandler structure for
49
                                 * the file (can't point directly to the
50
                                 * FileHandler structure because it could
51
                                 * go away while the event is queued). */
52
} FileHandlerEvent;
53
 
54
/*
55
 * The following static structure contains the state information for the
56
 * select based implementation of the Tcl notifier.
57
 */
58
 
59
static struct {
60
    FileHandler *firstFileHandlerPtr;
61
                                /* Pointer to head of file handler list. */
62
    fd_mask checkMasks[3*MASK_SIZE];
63
                                /* This array is used to build up the masks
64
                                 * to be used in the next call to select.
65
                                 * Bits are set in response to calls to
66
                                 * Tcl_CreateFileHandler. */
67
    fd_mask readyMasks[3*MASK_SIZE];
68
                                /* This array reflects the readable/writable
69
                                 * conditions that were found to exist by the
70
                                 * last call to select. */
71
    int numFdBits;              /* Number of valid bits in checkMasks
72
                                 * (one more than highest fd for which
73
                                 * Tcl_WatchFile has been called). */
74
} notifier;
75
 
76
/*
77
 * The following static indicates whether this module has been initialized.
78
 */
79
 
80
static int initialized = 0;
81
 
82
/*
83
 * Static routines defined in this file.
84
 */
85
 
86
static void             InitNotifier _ANSI_ARGS_((void));
87
static void             NotifierExitHandler _ANSI_ARGS_((
88
                            ClientData clientData));
89
static int              FileHandlerEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
90
                            int flags));
91
 
92
/*
93
 *----------------------------------------------------------------------
94
 *
95
 * InitNotifier --
96
 *
97
 *      Initializes the notifier state.
98
 *
99
 * Results:
100
 *      None.
101
 *
102
 * Side effects:
103
 *      Creates a new exit handler.
104
 *
105
 *----------------------------------------------------------------------
106
 */
107
 
108
static void
109
InitNotifier()
110
{
111
    initialized = 1;
112
    memset(&notifier, 0, sizeof(notifier));
113
    Tcl_CreateExitHandler(NotifierExitHandler, NULL);
114
}
115
 
116
/*
117
 *----------------------------------------------------------------------
118
 *
119
 * NotifierExitHandler --
120
 *
121
 *      This function is called to cleanup the notifier state before
122
 *      Tcl is unloaded.
123
 *
124
 * Results:
125
 *      None.
126
 *
127
 * Side effects:
128
 *      Destroys the notifier window.
129
 *
130
 *----------------------------------------------------------------------
131
 */
132
 
133
static void
134
NotifierExitHandler(clientData)
135
    ClientData clientData;              /* Not used. */
136
{
137
    initialized = 0;
138
}
139
 
140
/*
141
 *----------------------------------------------------------------------
142
 *
143
 * Tcl_SetTimer --
144
 *
145
 *      This procedure sets the current notifier timer value.  This
146
 *      interface is not implemented in this notifier because we are
147
 *      always running inside of Tcl_DoOneEvent.
148
 *
149
 * Results:
150
 *      None.
151
 *
152
 * Side effects:
153
 *      None.
154
 *
155
 *----------------------------------------------------------------------
156
 */
157
 
158
void
159
Tcl_SetTimer(timePtr)
160
    Tcl_Time *timePtr;          /* Timeout value, may be NULL. */
161
{
162
    /*
163
     * The interval timer doesn't do anything in this implementation,
164
     * because the only event loop is via Tcl_DoOneEvent, which passes
165
     * timeout values to Tcl_WaitForEvent.
166
     */
167
}
168
 
169
/*
170
 *----------------------------------------------------------------------
171
 *
172
 * Tcl_CreateFileHandler --
173
 *
174
 *      This procedure registers a file handler with the Xt notifier.
175
 *
176
 * Results:
177
 *      None.
178
 *
179
 * Side effects:
180
 *      Creates a new file handler structure and registers one or more
181
 *      input procedures with Xt.
182
 *
183
 *----------------------------------------------------------------------
184
 */
185
 
186
void
187
Tcl_CreateFileHandler(fd, mask, proc, clientData)
188
    int fd;                     /* Handle of stream to watch. */
189
    int mask;                   /* OR'ed combination of TCL_READABLE,
190
                                 * TCL_WRITABLE, and TCL_EXCEPTION:
191
                                 * indicates conditions under which
192
                                 * proc should be called. */
193
    Tcl_FileProc *proc;         /* Procedure to call for each
194
                                 * selected event. */
195
    ClientData clientData;      /* Arbitrary data to pass to proc. */
196
{
197
    FileHandler *filePtr;
198
    int index, bit;
199
 
200
    if (!initialized) {
201
        InitNotifier();
202
    }
203
 
204
    for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
205
            filePtr = filePtr->nextPtr) {
206
        if (filePtr->fd == fd) {
207
            break;
208
        }
209
    }
210
    if (filePtr == NULL) {
211
        filePtr = (FileHandler*) ckalloc(sizeof(FileHandler)); /* MLK */
212
        filePtr->fd = fd;
213
        filePtr->readyMask = 0;
214
        filePtr->nextPtr = notifier.firstFileHandlerPtr;
215
        notifier.firstFileHandlerPtr = filePtr;
216
    }
217
    filePtr->proc = proc;
218
    filePtr->clientData = clientData;
219
    filePtr->mask = mask;
220
 
221
    /*
222
     * Update the check masks for this file.
223
     */
224
 
225
    index = fd/(NBBY*sizeof(fd_mask));
226
    bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
227
    if (mask & TCL_READABLE) {
228
        notifier.checkMasks[index] |= bit;
229
    } else {
230
        notifier.checkMasks[index] &= ~bit;
231
    }
232
    if (mask & TCL_WRITABLE) {
233
        (notifier.checkMasks+MASK_SIZE)[index] |= bit;
234
    } else {
235
        (notifier.checkMasks+MASK_SIZE)[index] &= ~bit;
236
    }
237
    if (mask & TCL_EXCEPTION) {
238
        (notifier.checkMasks+2*(MASK_SIZE))[index] |= bit;
239
    } else {
240
        (notifier.checkMasks+2*(MASK_SIZE))[index] &= ~bit;
241
    }
242
    if (notifier.numFdBits <= fd) {
243
        notifier.numFdBits = fd+1;
244
    }
245
}
246
 
247
/*
248
 *----------------------------------------------------------------------
249
 *
250
 * Tcl_DeleteFileHandler --
251
 *
252
 *      Cancel a previously-arranged callback arrangement for
253
 *      a file.
254
 *
255
 * Results:
256
 *      None.
257
 *
258
 * Side effects:
259
 *      If a callback was previously registered on file, remove it.
260
 *
261
 *----------------------------------------------------------------------
262
 */
263
 
264
void
265
Tcl_DeleteFileHandler(fd)
266
    int fd;             /* Stream id for which to remove callback procedure. */
267
{
268
    FileHandler *filePtr, *prevPtr;
269
    int index, bit, i;
270
    unsigned long flags;
271
 
272
    if (!initialized) {
273
        InitNotifier();
274
    }
275
 
276
    /*
277
     * Find the entry for the given file (and return if there
278
     * isn't one).
279
     */
280
 
281
    for (prevPtr = NULL, filePtr = notifier.firstFileHandlerPtr; ;
282
            prevPtr = filePtr, filePtr = filePtr->nextPtr) {
283
        if (filePtr == NULL) {
284
            return;
285
        }
286
        if (filePtr->fd == fd) {
287
            break;
288
        }
289
    }
290
 
291
    /*
292
     * Update the check masks for this file.
293
     */
294
 
295
    index = fd/(NBBY*sizeof(fd_mask));
296
    bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
297
 
298
    if (filePtr->mask & TCL_READABLE) {
299
        notifier.checkMasks[index] &= ~bit;
300
    }
301
    if (filePtr->mask & TCL_WRITABLE) {
302
        (notifier.checkMasks+MASK_SIZE)[index] &= ~bit;
303
    }
304
    if (filePtr->mask & TCL_EXCEPTION) {
305
        (notifier.checkMasks+2*(MASK_SIZE))[index] &= ~bit;
306
    }
307
 
308
    /*
309
     * Find current max fd.
310
     */
311
 
312
    if (fd+1 == notifier.numFdBits) {
313
        for (notifier.numFdBits = 0; index >= 0; index--) {
314
            flags = notifier.checkMasks[index]
315
                | (notifier.checkMasks+MASK_SIZE)[index]
316
                | (notifier.checkMasks+2*(MASK_SIZE))[index];
317
            if (flags) {
318
                for (i = (NBBY*sizeof(fd_mask)); i > 0; i--) {
319
                    if (flags & (((unsigned long)1) << (i-1))) {
320
                        break;
321
                    }
322
                }
323
                notifier.numFdBits = index * (NBBY*sizeof(fd_mask)) + i;
324
                break;
325
            }
326
        }
327
    }
328
 
329
    /*
330
     * Clean up information in the callback record.
331
     */
332
 
333
    if (prevPtr == NULL) {
334
        notifier.firstFileHandlerPtr = filePtr->nextPtr;
335
    } else {
336
        prevPtr->nextPtr = filePtr->nextPtr;
337
    }
338
    ckfree((char *) filePtr);
339
}
340
 
341
/*
342
 *----------------------------------------------------------------------
343
 *
344
 * FileHandlerEventProc --
345
 *
346
 *      This procedure is called by Tcl_ServiceEvent when a file event
347
 *      reaches the front of the event queue.  This procedure is
348
 *      responsible for actually handling the event by invoking the
349
 *      callback for the file handler.
350
 *
351
 * Results:
352
 *      Returns 1 if the event was handled, meaning it should be removed
353
 *      from the queue.  Returns 0 if the event was not handled, meaning
354
 *      it should stay on the queue.  The only time the event isn't
355
 *      handled is if the TCL_FILE_EVENTS flag bit isn't set.
356
 *
357
 * Side effects:
358
 *      Whatever the file handler's callback procedure does.
359
 *
360
 *----------------------------------------------------------------------
361
 */
362
 
363
static int
364
FileHandlerEventProc(evPtr, flags)
365
    Tcl_Event *evPtr;           /* Event to service. */
366
    int flags;                  /* Flags that indicate what events to
367
                                 * handle, such as TCL_FILE_EVENTS. */
368
{
369
    FileHandler *filePtr;
370
    FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr;
371
    int mask;
372
 
373
    if (!(flags & TCL_FILE_EVENTS)) {
374
        return 0;
375
    }
376
 
377
    /*
378
     * Search through the file handlers to find the one whose handle matches
379
     * the event.  We do this rather than keeping a pointer to the file
380
     * handler directly in the event, so that the handler can be deleted
381
     * while the event is queued without leaving a dangling pointer.
382
     */
383
 
384
    for (filePtr = notifier.firstFileHandlerPtr; filePtr != NULL;
385
            filePtr = filePtr->nextPtr) {
386
        if (filePtr->fd != fileEvPtr->fd) {
387
            continue;
388
        }
389
 
390
        /*
391
         * The code is tricky for two reasons:
392
         * 1. The file handler's desired events could have changed
393
         *    since the time when the event was queued, so AND the
394
         *    ready mask with the desired mask.
395
         * 2. The file could have been closed and re-opened since
396
         *    the time when the event was queued.  This is why the
397
         *    ready mask is stored in the file handler rather than
398
         *    the queued event:  it will be zeroed when a new
399
         *    file handler is created for the newly opened file.
400
         */
401
 
402
        mask = filePtr->readyMask & filePtr->mask;
403
        filePtr->readyMask = 0;
404
        if (mask != 0) {
405
            (*filePtr->proc)(filePtr->clientData, mask);
406
        }
407
        break;
408
    }
409
    return 1;
410
}
411
 
412
/*
413
 *----------------------------------------------------------------------
414
 *
415
 * Tcl_WaitForEvent --
416
 *
417
 *      This function is called by Tcl_DoOneEvent to wait for new
418
 *      events on the message queue.  If the block time is 0, then
419
 *      Tcl_WaitForEvent just polls without blocking.
420
 *
421
 * Results:
422
 *      Returns -1 if the select would block forever, otherwise
423
 *      returns 0.
424
 *
425
 * Side effects:
426
 *      Queues file events that are detected by the select.
427
 *
428
 *----------------------------------------------------------------------
429
 */
430
 
431
int
432
Tcl_WaitForEvent(timePtr)
433
    Tcl_Time *timePtr;          /* Maximum block time, or NULL. */
434
{
435
    FileHandler *filePtr;
436
    FileHandlerEvent *fileEvPtr;
437
    struct timeval timeout, *timeoutPtr;
438
    int bit, index, mask, numFound;
439
 
440
    if (!initialized) {
441
        InitNotifier();
442
    }
443
 
444
    /*
445
     * Set up the timeout structure.  Note that if there are no events to
446
     * check for, we return with a negative result rather than blocking
447
     * forever.
448
     */
449
 
450
    if (timePtr) {
451
        timeout.tv_sec = timePtr->sec;
452
        timeout.tv_usec = timePtr->usec;
453
        timeoutPtr = &timeout;
454
    } else if (notifier.numFdBits == 0) {
455
        return -1;
456
    } else {
457
        timeoutPtr = NULL;
458
    }
459
 
460
    memcpy((VOID *) notifier.readyMasks, (VOID *) notifier.checkMasks,
461
            3*MASK_SIZE*sizeof(fd_mask));
462
    numFound = select(notifier.numFdBits,
463
            (SELECT_MASK *) &notifier.readyMasks[0],
464
            (SELECT_MASK *) &notifier.readyMasks[MASK_SIZE],
465
            (SELECT_MASK *) &notifier.readyMasks[2*MASK_SIZE], timeoutPtr);
466
 
467
    /*
468
     * Some systems don't clear the masks after an error, so
469
     * we have to do it here.
470
     */
471
 
472
    if (numFound == -1) {
473
        memset((VOID *) notifier.readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
474
    }
475
 
476
    /*
477
     * Queue all detected file events before returning.
478
     */
479
 
480
    for (filePtr = notifier.firstFileHandlerPtr;
481
            (filePtr != NULL) && (numFound > 0);
482
            filePtr = filePtr->nextPtr) {
483
        index = filePtr->fd / (NBBY*sizeof(fd_mask));
484
        bit = 1 << (filePtr->fd % (NBBY*sizeof(fd_mask)));
485
        mask = 0;
486
 
487
        if (notifier.readyMasks[index] & bit) {
488
            mask |= TCL_READABLE;
489
        }
490
        if ((notifier.readyMasks+MASK_SIZE)[index] & bit) {
491
            mask |= TCL_WRITABLE;
492
        }
493
        if ((notifier.readyMasks+2*(MASK_SIZE))[index] & bit) {
494
            mask |= TCL_EXCEPTION;
495
        }
496
 
497
        if (!mask) {
498
            continue;
499
        } else {
500
            numFound--;
501
        }
502
 
503
        /*
504
         * Don't bother to queue an event if the mask was previously
505
         * non-zero since an event must still be on the queue.
506
         */
507
 
508
        if (filePtr->readyMask == 0) {
509
            fileEvPtr = (FileHandlerEvent *) ckalloc(
510
                sizeof(FileHandlerEvent));
511
            fileEvPtr->header.proc = FileHandlerEventProc;
512
            fileEvPtr->fd = filePtr->fd;
513
            Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL);
514
        }
515
        filePtr->readyMask = mask;
516
    }
517
    return 0;
518
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.