tclIORChan.cGo to the documentation of this file.00001 /* 00002 * tclIORChan.c -- 00003 * 00004 * This file contains the implementation of Tcl's generic channel 00005 * reflection code, which allows the implementation of Tcl channels in 00006 * Tcl code. 00007 * 00008 * Parts of this file are based on code contributed by Jean-Claude 00009 * Wippler. 00010 * 00011 * See TIP #219 for the specification of this functionality. 00012 * 00013 * Copyright (c) 2004-2005 ActiveState, a divison of Sophos 00014 * 00015 * See the file "license.terms" for information on usage and redistribution of 00016 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00017 * 00018 * RCS: @(#) $Id: tclIORChan.c,v 1.27 2007/12/13 15:23:18 dgp Exp $ 00019 */ 00020 00021 #include <tclInt.h> 00022 #include <tclIO.h> 00023 #include <assert.h> 00024 00025 #ifndef EINVAL 00026 #define EINVAL 9 00027 #endif 00028 #ifndef EOK 00029 #define EOK 0 00030 #endif 00031 00032 /* 00033 * Signatures of all functions used in the C layer of the reflection. 00034 */ 00035 00036 static int ReflectClose(ClientData clientData, 00037 Tcl_Interp *interp); 00038 static int ReflectInput(ClientData clientData, char *buf, 00039 int toRead, int *errorCodePtr); 00040 static int ReflectOutput(ClientData clientData, const char *buf, 00041 int toWrite, int *errorCodePtr); 00042 static void ReflectWatch(ClientData clientData, int mask); 00043 static int ReflectBlock(ClientData clientData, int mode); 00044 static Tcl_WideInt ReflectSeekWide(ClientData clientData, 00045 Tcl_WideInt offset, int mode, int *errorCodePtr); 00046 static int ReflectSeek(ClientData clientData, long offset, 00047 int mode, int *errorCodePtr); 00048 static int ReflectGetOption(ClientData clientData, 00049 Tcl_Interp *interp, const char *optionName, 00050 Tcl_DString *dsPtr); 00051 static int ReflectSetOption(ClientData clientData, 00052 Tcl_Interp *interp, const char *optionName, 00053 const char *newValue); 00054 00055 /* 00056 * The C layer channel type/driver definition used by the reflection. This is 00057 * a version 3 structure. 00058 */ 00059 00060 static Tcl_ChannelType tclRChannelType = { 00061 "tclrchannel", /* Type name. */ 00062 TCL_CHANNEL_VERSION_5, /* v5 channel */ 00063 ReflectClose, /* Close channel, clean instance data */ 00064 ReflectInput, /* Handle read request */ 00065 ReflectOutput, /* Handle write request */ 00066 ReflectSeek, /* Move location of access point. NULL'able */ 00067 ReflectSetOption, /* Set options. NULL'able */ 00068 ReflectGetOption, /* Get options. NULL'able */ 00069 ReflectWatch, /* Initialize notifier */ 00070 NULL, /* Get OS handle from the channel. NULL'able */ 00071 NULL, /* No close2 support. NULL'able */ 00072 ReflectBlock, /* Set blocking/nonblocking. NULL'able */ 00073 NULL, /* Flush channel. Not used by core. NULL'able */ 00074 NULL, /* Handle events. NULL'able */ 00075 ReflectSeekWide, /* Move access point (64 bit). NULL'able */ 00076 NULL, /* thread action */ 00077 NULL, /* truncate */ 00078 }; 00079 00080 /* 00081 * Instance data for a reflected channel. =========================== 00082 */ 00083 00084 typedef struct { 00085 Tcl_Channel chan; /* Back reference to generic channel 00086 * structure. */ 00087 Tcl_Interp *interp; /* Reference to the interpreter containing the 00088 * Tcl level part of the channel. */ 00089 #ifdef TCL_THREADS 00090 Tcl_ThreadId thread; /* Thread the 'interp' belongs to. */ 00091 #endif 00092 00093 /* See [==] as well. 00094 * Storage for the command prefix and the additional words required for 00095 * the invocation of methods in the command handler. 00096 * 00097 * argv [0] ... [.] | [argc-2] [argc-1] | [argc] [argc+2] 00098 * cmd ... pfx | method chan | detail1 detail2 00099 * ~~~~ CT ~~~ ~~ CT ~~ 00100 * 00101 * CT = Belongs to the 'Command handler Thread'. 00102 */ 00103 00104 int argc; /* Number of preallocated words - 2 */ 00105 Tcl_Obj **argv; /* Preallocated array for calling the handler. 00106 * args[0] is placeholder for cmd word. 00107 * Followed by the arguments in the prefix, 00108 * plus 4 placeholders for method, channel, 00109 * and at most two varying (method specific) 00110 * words. */ 00111 int methods; /* Bitmask of supported methods */ 00112 00113 /* 00114 * NOTE (9): Should we have predefined shared literals for the method 00115 * names? 00116 */ 00117 00118 int mode; /* Mask of R/W mode */ 00119 int interest; /* Mask of events the channel is interested 00120 * in. */ 00121 00122 /* 00123 * Note regarding the usage of timers. 00124 * 00125 * Most channel implementations need a timer in the C level to ensure that 00126 * data in buffers is flushed out through the generation of fake file 00127 * events. 00128 * 00129 * See 'rechan', 'memchan', etc. 00130 * 00131 * Here this is _not_ required. Interest in events is posted to the Tcl 00132 * level via 'watch'. And posting of events is possible from the Tcl level 00133 * as well, via 'chan postevent'. This means that the generation of all 00134 * events, fake or not, timer based or not, is completely in the hands of 00135 * the Tcl level. Therefore no timer here. 00136 */ 00137 } ReflectedChannel; 00138 00139 /* 00140 * Structure of the table maping from channel handles to reflected 00141 * channels. Each interpreter which has the handler command for one or more 00142 * reflected channels records them in such a table, so that 'chan postevent' 00143 * is able to find them even if the actual channel was moved to a different 00144 * interpreter and/or thread. 00145 * 00146 * The table is reachable via the standard interpreter AssocData, the key is 00147 * defined below. 00148 */ 00149 00150 typedef struct { 00151 Tcl_HashTable map; 00152 } ReflectedChannelMap; 00153 00154 #define RCMKEY "ReflectedChannelMap" 00155 00156 /* 00157 * Event literals. ================================================== 00158 */ 00159 00160 static const char *eventOptions[] = { 00161 "read", "write", NULL 00162 }; 00163 typedef enum { 00164 EVENT_READ, EVENT_WRITE 00165 } EventOption; 00166 00167 /* 00168 * Method literals. ================================================== 00169 */ 00170 00171 static const char *methodNames[] = { 00172 "blocking", /* OPT */ 00173 "cget", /* OPT \/ Together or none */ 00174 "cgetall", /* OPT /\ of these two */ 00175 "configure", /* OPT */ 00176 "finalize", /* */ 00177 "initialize", /* */ 00178 "read", /* OPT */ 00179 "seek", /* OPT */ 00180 "watch", /* */ 00181 "write", /* OPT */ 00182 NULL 00183 }; 00184 typedef enum { 00185 METH_BLOCKING, 00186 METH_CGET, 00187 METH_CGETALL, 00188 METH_CONFIGURE, 00189 METH_FINAL, 00190 METH_INIT, 00191 METH_READ, 00192 METH_SEEK, 00193 METH_WATCH, 00194 METH_WRITE, 00195 } MethodName; 00196 00197 #define FLAG(m) (1 << (m)) 00198 #define REQUIRED_METHODS \ 00199 (FLAG(METH_INIT) | FLAG(METH_FINAL) | FLAG(METH_WATCH)) 00200 #define NULLABLE_METHODS \ 00201 (FLAG(METH_BLOCKING) | FLAG(METH_SEEK) | \ 00202 FLAG(METH_CONFIGURE) | FLAG(METH_CGET) | FLAG(METH_CGETALL)) 00203 00204 #define RANDW \ 00205 (TCL_READABLE | TCL_WRITABLE) 00206 00207 #define IMPLIES(a,b) ((!(a)) || (b)) 00208 #define NEGIMPL(a,b) 00209 #define HAS(x,f) (x & FLAG(f)) 00210 00211 #ifdef TCL_THREADS 00212 /* 00213 * Thread specific types and structures. 00214 * 00215 * We are here essentially creating a very specific implementation of 'thread 00216 * send'. 00217 */ 00218 00219 /* 00220 * Enumeration of all operations which can be forwarded. 00221 */ 00222 00223 typedef enum { 00224 ForwardedClose, 00225 ForwardedInput, 00226 ForwardedOutput, 00227 ForwardedSeek, 00228 ForwardedWatch, 00229 ForwardedBlock, 00230 ForwardedSetOpt, 00231 ForwardedGetOpt, 00232 ForwardedGetOptAll 00233 } ForwardedOperation; 00234 00235 /* 00236 * Event used to forward driver invocations to the thread actually managing 00237 * the channel. We cannot construct the command to execute and forward that. 00238 * Because then it will contain a mixture of Tcl_Obj's belonging to both the 00239 * command handler thread (CT), and the thread managing the channel (MT), 00240 * executed in CT. Tcl_Obj's are not allowed to cross thread boundaries. So we 00241 * forward an operation code, the argument details, and reference to results. 00242 * The command is assembled in the CT and belongs fully to that thread. No 00243 * sharing problems. 00244 */ 00245 00246 typedef struct ForwardParamBase { 00247 int code; /* O: Ok/Fail of the cmd handler */ 00248 char *msgStr; /* O: Error message for handler failure */ 00249 int mustFree; /* O: True if msgStr is allocated, false if 00250 * otherwise (static). */ 00251 } ForwardParamBase; 00252 00253 /* 00254 * Operation specific parameter/result structures. (These are "subtypes" of 00255 * ForwardParamBase. Where an operation does not need any special types, it 00256 * has no "subtype" and just uses ForwardParamBase, as listed above.) 00257 */ 00258 00259 struct ForwardParamInput { 00260 ForwardParamBase base; /* "Supertype". MUST COME FIRST. */ 00261 char *buf; /* O: Where to store the read bytes */ 00262 int toRead; /* I: #bytes to read, 00263 * O: #bytes actually read */ 00264 }; 00265 struct ForwardParamOutput { 00266 ForwardParamBase base; /* "Supertype". MUST COME FIRST. */ 00267 const char *buf; /* I: Where the bytes to write come from */ 00268 int toWrite; /* I: #bytes to write, 00269 * O: #bytes actually written */ 00270 }; 00271 struct ForwardParamSeek { 00272 ForwardParamBase base; /* "Supertype". MUST COME FIRST. */ 00273 int seekMode; /* I: How to seek */ 00274 Tcl_WideInt offset; /* I: Where to seek, 00275 * O: New location */ 00276 }; 00277 struct ForwardParamWatch { 00278 ForwardParamBase base; /* "Supertype". MUST COME FIRST. */ 00279 int mask; /* I: What events to watch for */ 00280 }; 00281 struct ForwardParamBlock { 00282 ForwardParamBase base; /* "Supertype". MUST COME FIRST. */ 00283 int nonblocking; /* I: What mode to activate */ 00284 }; 00285 struct ForwardParamSetOpt { 00286 ForwardParamBase base; /* "Supertype". MUST COME FIRST. */ 00287 const char *name; /* Name of option to set */ 00288 const char *value; /* Value to set */ 00289 }; 00290 struct ForwardParamGetOpt { 00291 ForwardParamBase base; /* "Supertype". MUST COME FIRST. */ 00292 const char *name; /* Name of option to get, maybe NULL */ 00293 Tcl_DString *value; /* Result */ 00294 }; 00295 00296 /* 00297 * Now join all these together in a single union for convenience. 00298 */ 00299 00300 typedef union ForwardParam { 00301 ForwardParamBase base; 00302 struct ForwardParamInput input; 00303 struct ForwardParamOutput output; 00304 struct ForwardParamSeek seek; 00305 struct ForwardParamWatch watch; 00306 struct ForwardParamBlock block; 00307 struct ForwardParamSetOpt setOpt; 00308 struct ForwardParamGetOpt getOpt; 00309 } ForwardParam; 00310 00311 /* 00312 * Forward declaration. 00313 */ 00314 00315 typedef struct ForwardingResult ForwardingResult; 00316 00317 /* 00318 * General event structure, with reference to operation specific data. 00319 */ 00320 00321 typedef struct ForwardingEvent { 00322 Tcl_Event event; /* Basic event data, has to be first item */ 00323 ForwardingResult *resultPtr; 00324 ForwardedOperation op; /* Forwarded driver operation */ 00325 ReflectedChannel *rcPtr; /* Channel instance */ 00326 ForwardParam *param; /* Packaged arguments and return values, a 00327 * ForwardParam pointer. */ 00328 } ForwardingEvent; 00329 00330 /* 00331 * Structure to manage the result of the forwarding. This is not the result of 00332 * the operation itself, but about the success of the forward event itself. 00333 * The event can be successful, even if the operation which was forwarded 00334 * failed. It is also there to manage the synchronization between the involved 00335 * threads. 00336 */ 00337 00338 struct ForwardingResult { 00339 Tcl_ThreadId src; /* Originating thread. */ 00340 Tcl_ThreadId dst; /* Thread the op was forwarded to. */ 00341 Tcl_Condition done; /* Condition variable the forwarder blocks 00342 * on. */ 00343 int result; /* TCL_OK or TCL_ERROR */ 00344 ForwardingEvent *evPtr; /* Event the result belongs to. */ 00345 ForwardingResult *prevPtr, *nextPtr; 00346 /* Links into the list of pending forwarded 00347 * results. */ 00348 }; 00349 00350 /* 00351 * List of forwarded operations which have not completed yet, plus the mutex 00352 * to protect the access to this process global list. 00353 */ 00354 00355 static ForwardingResult *forwardList = NULL; 00356 TCL_DECLARE_MUTEX(rcForwardMutex) 00357 00358 /* 00359 * Function containing the generic code executing a forward, and wrapper 00360 * macros for the actual operations we wish to forward. Uses ForwardProc as 00361 * the event function executed by the thread receiving a forwarding event 00362 * (which executes the appropriate function and collects the result, if any). 00363 * 00364 * The two ExitProcs are handlers so that things do not deadlock when either 00365 * thread involved in the forwarding exits. They also clean things up so that 00366 * we don't leak resources when threads go away. 00367 */ 00368 00369 static void ForwardOpToOwnerThread(ReflectedChannel *rcPtr, 00370 ForwardedOperation op, const VOID *param); 00371 static int ForwardProc(Tcl_Event *evPtr, int mask); 00372 static void SrcExitProc(ClientData clientData); 00373 static void DstExitProc(ClientData clientData); 00374 00375 #define FreeReceivedError(p) \ 00376 if ((p)->base.mustFree) { \ 00377 ckfree((p)->base.msgStr); \ 00378 } 00379 #define PassReceivedErrorInterp(i,p) \ 00380 if ((i) != NULL) { \ 00381 Tcl_SetChannelErrorInterp((i), \ 00382 Tcl_NewStringObj((p)->base.msgStr, -1)); \ 00383 } \ 00384 FreeReceivedError(p) 00385 #define PassReceivedError(c,p) \ 00386 Tcl_SetChannelError((c), Tcl_NewStringObj((p)->base.msgStr, -1)); \ 00387 FreeReceivedError(p) 00388 #define ForwardSetStaticError(p,emsg) \ 00389 (p)->base.code = TCL_ERROR; \ 00390 (p)->base.mustFree = 0; \ 00391 (p)->base.msgStr = (char *) (emsg) 00392 #define ForwardSetDynamicError(p,emsg) \ 00393 (p)->base.code = TCL_ERROR; \ 00394 (p)->base.mustFree = 1; \ 00395 (p)->base.msgStr = (char *) (emsg) 00396 00397 static void ForwardSetObjError(ForwardParam *p, Tcl_Obj *objPtr); 00398 #endif /* TCL_THREADS */ 00399 00400 #define SetChannelErrorStr(c,msgStr) \ 00401 Tcl_SetChannelError((c), Tcl_NewStringObj((msgStr), -1)) 00402 00403 static Tcl_Obj * MarshallError(Tcl_Interp *interp); 00404 static void UnmarshallErrorResult(Tcl_Interp *interp, 00405 Tcl_Obj *msgObj); 00406 00407 /* 00408 * Static functions for this file: 00409 */ 00410 00411 static int EncodeEventMask(Tcl_Interp *interp, 00412 const char *objName, Tcl_Obj *obj, int *mask); 00413 static Tcl_Obj * DecodeEventMask(int mask); 00414 static ReflectedChannel * NewReflectedChannel(Tcl_Interp *interp, 00415 Tcl_Obj *cmdpfxObj, int mode, Tcl_Obj *handleObj); 00416 static Tcl_Obj * NextHandle(void); 00417 static void FreeReflectedChannel(ReflectedChannel *rcPtr); 00418 static int InvokeTclMethod(ReflectedChannel *rcPtr, 00419 const char *method, Tcl_Obj *argOneObj, 00420 Tcl_Obj *argTwoObj, Tcl_Obj **resultObjPtr); 00421 00422 static ReflectedChannelMap * GetReflectedChannelMap(Tcl_Interp *interp); 00423 static void DeleteReflectedChannelMap(ClientData clientData, 00424 Tcl_Interp *interp); 00425 00426 /* 00427 * Global constant strings (messages). ================== 00428 * These string are used directly as bypass errors, thus they have to be valid 00429 * Tcl lists where the last element is the message itself. Hence the 00430 * list-quoting to keep the words of the message together. See also [x]. 00431 */ 00432 00433 static const char *msg_read_unsup = "{read not supported by Tcl driver}"; 00434 static const char *msg_read_toomuch = "{read delivered more than requested}"; 00435 static const char *msg_write_unsup = "{write not supported by Tcl driver}"; 00436 static const char *msg_write_toomuch = "{write wrote more than requested}"; 00437 static const char *msg_seek_beforestart = "{Tried to seek before origin}"; 00438 #ifdef TCL_THREADS 00439 static const char *msg_send_originlost = "{Origin thread lost}"; 00440 static const char *msg_send_dstlost = "{Destination thread lost}"; 00441 #endif /* TCL_THREADS */ 00442 00443 /* 00444 * Main methods to plug into the 'chan' ensemble'. ================== 00445 */ 00446 00447 /* 00448 *---------------------------------------------------------------------- 00449 * 00450 * TclChanCreateObjCmd -- 00451 * 00452 * This function is invoked to process the "chan create" Tcl command. 00453 * See the user documentation for details on what it does. 00454 * 00455 * Results: 00456 * A standard Tcl result. The handle of the new channel is placed in the 00457 * interp result. 00458 * 00459 * Side effects: 00460 * Creates a new channel. 00461 * 00462 *---------------------------------------------------------------------- 00463 */ 00464 00465 int 00466 TclChanCreateObjCmd( 00467 ClientData clientData, 00468 Tcl_Interp *interp, 00469 int objc, 00470 Tcl_Obj *const *objv) 00471 { 00472 ReflectedChannel *rcPtr; /* Instance data of the new channel */ 00473 Tcl_Obj *rcId; /* Handle of the new channel */ 00474 int mode; /* R/W mode of new channel. Has to match 00475 * abilities of handler commands */ 00476 Tcl_Obj *cmdObj; /* Command prefix, list of words */ 00477 Tcl_Obj *cmdNameObj; /* Command name */ 00478 Tcl_Channel chan; /* Token for the new channel */ 00479 Tcl_Obj *modeObj; /* mode in obj form for method call */ 00480 int listc; /* Result of 'initialize', and of */ 00481 Tcl_Obj **listv; /* its sublist in the 2nd element */ 00482 int methIndex; /* Encoded method name */ 00483 int result; /* Result code for 'initialize' */ 00484 Tcl_Obj *resObj; /* Result data for 'initialize' */ 00485 int methods; /* Bitmask for supported methods. */ 00486 Channel *chanPtr; /* 'chan' resolved to internal struct. */ 00487 Tcl_Obj *err; /* Error message */ 00488 ReflectedChannelMap* rcmPtr; /* Map of reflected channels with handlers in this interp */ 00489 Tcl_HashEntry* hPtr; /* Entry in the above map */ 00490 int isNew; /* Placeholder. */ 00491 00492 /* 00493 * Syntax: chan create MODE CMDPREFIX 00494 * [0] [1] [2] [3] 00495 * 00496 * Actually: rCreate MODE CMDPREFIX 00497 * [0] [1] [2] 00498 */ 00499 00500 #define MODE (1) 00501 #define CMD (2) 00502 00503 /* 00504 * Number of arguments... 00505 */ 00506 00507 if (objc != 3) { 00508 Tcl_WrongNumArgs(interp, 1, objv, "mode cmdprefix"); 00509 return TCL_ERROR; 00510 } 00511 00512 /* 00513 * First argument is a list of modes. Allowed entries are "read", "write". 00514 * Expect at least one list element. Abbreviations are ok. 00515 */ 00516 00517 modeObj = objv[MODE]; 00518 if (EncodeEventMask(interp, "mode", objv[MODE], &mode) != TCL_OK) { 00519 return TCL_ERROR; 00520 } 00521 00522 /* 00523 * Second argument is command prefix, i.e. list of words, first word is 00524 * name of handler command, other words are fixed arguments. Run the 00525 * 'initialize' method to get the list of supported methods. Validate 00526 * this. 00527 */ 00528 00529 cmdObj = objv[CMD]; 00530 00531 /* 00532 * Basic check that the command prefix truly is a list. 00533 */ 00534 00535 if (Tcl_ListObjIndex(interp, cmdObj, 0, &cmdNameObj) != TCL_OK) { 00536 return TCL_ERROR; 00537 } 00538 00539 /* 00540 * Now create the channel. 00541 */ 00542 00543 rcId = NextHandle(); 00544 rcPtr = NewReflectedChannel(interp, cmdObj, mode, rcId); 00545 chan = Tcl_CreateChannel(&tclRChannelType, TclGetString(rcId), rcPtr, 00546 mode); 00547 rcPtr->chan = chan; 00548 chanPtr = (Channel *) chan; 00549 00550 /* 00551 * Invoke 'initialize' and validate that the handler is present and ok. 00552 * Squash the channel if not. 00553 * 00554 * Note: The conversion of 'mode' back into a Tcl_Obj ensures that 00555 * 'initialize' is invoked with canonical mode names, and no 00556 * abbreviations. Using modeObj directly could feed abbreviations into the 00557 * handler, and the handler is not specified to handle such. 00558 */ 00559 00560 modeObj = DecodeEventMask(mode); 00561 result = InvokeTclMethod(rcPtr, "initialize", modeObj, NULL, &resObj); 00562 Tcl_DecrRefCount(modeObj); 00563 if (result != TCL_OK) { 00564 UnmarshallErrorResult(interp, resObj); 00565 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 00566 goto error; 00567 } 00568 00569 /* 00570 * Verify the result. 00571 * - List, of method names. Convert to mask. 00572 * Check for non-optionals through the mask. 00573 * Compare open mode against optional r/w. 00574 */ 00575 00576 if (Tcl_ListObjGetElements(NULL, resObj, &listc, &listv) != TCL_OK) { 00577 TclNewLiteralStringObj(err, "chan handler \""); 00578 Tcl_AppendObjToObj(err, cmdObj); 00579 Tcl_AppendToObj(err, " initialize\" returned non-list: ", -1); 00580 Tcl_AppendObjToObj(err, resObj); 00581 Tcl_SetObjResult(interp, err); 00582 Tcl_DecrRefCount(resObj); 00583 goto error; 00584 } 00585 00586 methods = 0; 00587 while (listc > 0) { 00588 if (Tcl_GetIndexFromObj(interp, listv[listc-1], methodNames, 00589 "method", TCL_EXACT, &methIndex) != TCL_OK) { 00590 TclNewLiteralStringObj(err, "chan handler \""); 00591 Tcl_AppendObjToObj(err, cmdObj); 00592 Tcl_AppendToObj(err, " initialize\" returned ", -1); 00593 Tcl_AppendObjToObj(err, Tcl_GetObjResult(interp)); 00594 Tcl_SetObjResult(interp, err); 00595 Tcl_DecrRefCount(resObj); 00596 goto error; 00597 } 00598 00599 methods |= FLAG(methIndex); 00600 listc--; 00601 } 00602 Tcl_DecrRefCount(resObj); 00603 00604 if ((REQUIRED_METHODS & methods) != REQUIRED_METHODS) { 00605 TclNewLiteralStringObj(err, "chan handler \""); 00606 Tcl_AppendObjToObj(err, cmdObj); 00607 Tcl_AppendToObj(err, "\" does not support all required methods", -1); 00608 Tcl_SetObjResult(interp, err); 00609 goto error; 00610 } 00611 00612 if ((mode & TCL_READABLE) && !HAS(methods, METH_READ)) { 00613 TclNewLiteralStringObj(err, "chan handler \""); 00614 Tcl_AppendObjToObj(err, cmdObj); 00615 Tcl_AppendToObj(err, "\" lacks a \"read\" method", -1); 00616 Tcl_SetObjResult(interp, err); 00617 goto error; 00618 } 00619 00620 if ((mode & TCL_WRITABLE) && !HAS(methods, METH_WRITE)) { 00621 TclNewLiteralStringObj(err, "chan handler \""); 00622 Tcl_AppendObjToObj(err, cmdObj); 00623 Tcl_AppendToObj(err, "\" lacks a \"write\" method", -1); 00624 Tcl_SetObjResult(interp, err); 00625 goto error; 00626 } 00627 00628 if (!IMPLIES(HAS(methods, METH_CGET), HAS(methods, METH_CGETALL))) { 00629 TclNewLiteralStringObj(err, "chan handler \""); 00630 Tcl_AppendObjToObj(err, cmdObj); 00631 Tcl_AppendToObj(err, "\" supports \"cget\" but not \"cgetall\"", -1); 00632 Tcl_SetObjResult(interp, err); 00633 goto error; 00634 } 00635 00636 if (!IMPLIES(HAS(methods, METH_CGETALL), HAS(methods, METH_CGET))) { 00637 TclNewLiteralStringObj(err, "chan handler \""); 00638 Tcl_AppendObjToObj(err, cmdObj); 00639 Tcl_AppendToObj(err, "\" supports \"cgetall\" but not \"cget\"", -1); 00640 Tcl_SetObjResult(interp, err); 00641 goto error; 00642 } 00643 00644 Tcl_ResetResult(interp); 00645 00646 /* 00647 * Everything is fine now. 00648 */ 00649 00650 rcPtr->methods = methods; 00651 00652 if ((methods & NULLABLE_METHODS) != NULLABLE_METHODS) { 00653 /* 00654 * Some of the nullable methods are not supported. We clone the 00655 * channel type, null the associated C functions, and use the result 00656 * as the actual channel type. 00657 */ 00658 00659 Tcl_ChannelType *clonePtr = (Tcl_ChannelType *) 00660 ckalloc(sizeof(Tcl_ChannelType)); 00661 00662 memcpy(clonePtr, &tclRChannelType, sizeof(Tcl_ChannelType)); 00663 00664 if (!(methods & FLAG(METH_CONFIGURE))) { 00665 clonePtr->setOptionProc = NULL; 00666 } 00667 00668 if (!(methods & FLAG(METH_CGET)) && !(methods & FLAG(METH_CGETALL))) { 00669 clonePtr->getOptionProc = NULL; 00670 } 00671 if (!(methods & FLAG(METH_BLOCKING))) { 00672 clonePtr->blockModeProc = NULL; 00673 } 00674 if (!(methods & FLAG(METH_SEEK))) { 00675 clonePtr->seekProc = NULL; 00676 clonePtr->wideSeekProc = NULL; 00677 } 00678 00679 chanPtr->typePtr = clonePtr; 00680 } 00681 00682 /* 00683 * Register the channel in the I/O system, and in our our map for 'chan 00684 * postevent'. 00685 */ 00686 00687 Tcl_RegisterChannel(interp, chan); 00688 00689 rcmPtr = GetReflectedChannelMap (interp); 00690 hPtr = Tcl_CreateHashEntry(&rcmPtr->map, 00691 chanPtr->state->channelName, &isNew); 00692 if (!isNew) { 00693 if (chanPtr != Tcl_GetHashValue(hPtr)) { 00694 Tcl_Panic("TclChanCreateObjCmd: duplicate channel names"); 00695 } 00696 } 00697 Tcl_SetHashValue(hPtr, chan); 00698 00699 /* 00700 * Return handle as result of command. 00701 */ 00702 00703 Tcl_SetObjResult(interp, rcId); 00704 return TCL_OK; 00705 00706 error: 00707 /* 00708 * Signal to ReflectClose to not call 'finalize'. 00709 */ 00710 00711 rcPtr->methods = 0; 00712 Tcl_Close(interp, chan); 00713 return TCL_ERROR; 00714 00715 #undef MODE 00716 #undef CMD 00717 } 00718 00719 /* 00720 *---------------------------------------------------------------------- 00721 * 00722 * TclChanPostEventObjCmd -- 00723 * 00724 * This function is invoked to process the "chan postevent" Tcl command. 00725 * See the user documentation for details on what it does. 00726 * 00727 * Results: 00728 * A standard Tcl result. 00729 * 00730 * Side effects: 00731 * Posts events to a reflected channel, invokes event handlers. The 00732 * latter implies that arbitrary side effects are possible. 00733 * 00734 *---------------------------------------------------------------------- 00735 */ 00736 00737 int 00738 TclChanPostEventObjCmd( 00739 ClientData clientData, 00740 Tcl_Interp *interp, 00741 int objc, 00742 Tcl_Obj *const *objv) 00743 { 00744 /* 00745 * Syntax: chan postevent CHANNEL EVENTSPEC 00746 * [0] [1] [2] [3] 00747 * 00748 * Actually: rPostevent CHANNEL EVENTSPEC 00749 * [0] [1] [2] 00750 * 00751 * where EVENTSPEC = {read write ...} (Abbreviations allowed as well). 00752 */ 00753 00754 #define CHAN (1) 00755 #define EVENT (2) 00756 00757 const char *chanId; /* Tcl level channel handle */ 00758 Tcl_Channel chan; /* Channel associated to the handle */ 00759 const Tcl_ChannelType *chanTypePtr; 00760 /* Its associated driver structure */ 00761 ReflectedChannel *rcPtr; /* Associated instance data */ 00762 int events; /* Mask of events to post */ 00763 ReflectedChannelMap* rcmPtr; /* Map of reflected channels with handlers in this interp */ 00764 Tcl_HashEntry* hPtr; /* Entry in the above map */ 00765 00766 /* 00767 * Number of arguments... 00768 */ 00769 00770 if (objc != 3) { 00771 Tcl_WrongNumArgs(interp, 1, objv, "channel eventspec"); 00772 return TCL_ERROR; 00773 } 00774 00775 /* 00776 * First argument is a channel, a reflected channel, and the call of this 00777 * command is done from the interp defining the channel handler cmd. 00778 */ 00779 00780 chanId = TclGetString(objv[CHAN]); 00781 00782 rcmPtr = GetReflectedChannelMap (interp); 00783 hPtr = Tcl_FindHashEntry (&rcmPtr->map, chanId); 00784 00785 if (hPtr == NULL) { 00786 Tcl_AppendResult(interp, "can not find reflected channel named \"", chanId, 00787 "\"", NULL); 00788 Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "CHANNEL", chanId, NULL); 00789 return TCL_ERROR; 00790 } 00791 00792 /* 00793 * Note that the search above subsumes several of the older checks, namely: 00794 * 00795 * (1) Does the channel handle refer to a reflected channel ? 00796 * (2) Is the post event issued from the interpreter holding the handler 00797 * of the reflected channel ? 00798 * 00799 * A successful search answers yes to both. Because the map holds only 00800 * handles of reflected channels, and only of such whose handler is 00801 * defined in this interpreter. 00802 * 00803 * We keep the old checks for both, for paranioa, but abort now instead of 00804 * throwing errors, as failure now means that our internal datastructures 00805 * have gone seriously haywire. 00806 */ 00807 00808 chan = Tcl_GetHashValue(hPtr); 00809 chanTypePtr = Tcl_GetChannelType(chan); 00810 00811 /* 00812 * We use a function referenced by the channel type as our cookie to 00813 * detect calls to non-reflecting channels. The channel type itself is not 00814 * suitable, as it might not be the static definition in this file, but a 00815 * clone thereof. And while we have reserved the name of the type nothing 00816 * in the core checks against violation, so someone else might have 00817 * created a channel type using our name, clashing with ourselves. 00818 */ 00819 00820 if (chanTypePtr->watchProc != &ReflectWatch) { 00821 Tcl_Panic ("TclChanPostEventObjCmd: channel is not a reflected channel"); 00822 } 00823 00824 rcPtr = (ReflectedChannel *) Tcl_GetChannelInstanceData(chan); 00825 00826 if (rcPtr->interp != interp) { 00827 Tcl_Panic ("TclChanPostEventObjCmd: postevent accepted for call from outside interpreter"); 00828 } 00829 00830 /* 00831 * Second argument is a list of events. Allowed entries are "read", 00832 * "write". Expect at least one list element. Abbreviations are ok. 00833 */ 00834 00835 if (EncodeEventMask(interp, "event", objv[EVENT], &events) != TCL_OK) { 00836 return TCL_ERROR; 00837 } 00838 00839 /* 00840 * Check that the channel is actually interested in the provided events. 00841 */ 00842 00843 if (events & ~rcPtr->interest) { 00844 Tcl_AppendResult(interp, "tried to post events channel \"", chanId, 00845 "\" is not interested in", NULL); 00846 return TCL_ERROR; 00847 } 00848 00849 /* 00850 * We have the channel and the events to post. 00851 */ 00852 00853 Tcl_NotifyChannel(chan, events); 00854 00855 /* 00856 * Squash interp results left by the event script. 00857 */ 00858 00859 Tcl_ResetResult(interp); 00860 return TCL_OK; 00861 00862 #undef CHAN 00863 #undef EVENT 00864 } 00865 00866 /* 00867 * Channel error message marshalling utilities. 00868 */ 00869 00870 static Tcl_Obj* 00871 MarshallError( 00872 Tcl_Interp *interp) 00873 { 00874 /* 00875 * Capture the result status of the interpreter into a string. => List of 00876 * options and values, followed by the error message. The result has 00877 * refCount 0. 00878 */ 00879 00880 Tcl_Obj *returnOpt = Tcl_GetReturnOptions(interp, TCL_ERROR); 00881 00882 /* 00883 * => returnOpt.refCount == 0. We can append directly. 00884 */ 00885 00886 Tcl_ListObjAppendElement(NULL, returnOpt, Tcl_GetObjResult(interp)); 00887 return returnOpt; 00888 } 00889 00890 static void 00891 UnmarshallErrorResult( 00892 Tcl_Interp *interp, 00893 Tcl_Obj *msgObj) 00894 { 00895 int lc; 00896 Tcl_Obj **lv; 00897 int explicitResult; 00898 int numOptions; 00899 00900 /* 00901 * Process the caught message. 00902 * 00903 * Syntax = (option value)... ?message? 00904 * 00905 * Bad syntax causes a panic. This is OK because the other side uses 00906 * Tcl_GetReturnOptions and list construction functions to marshall the 00907 * information; if we panic here, something has gone badly wrong already. 00908 */ 00909 00910 if (Tcl_ListObjGetElements(interp, msgObj, &lc, &lv) != TCL_OK) { 00911 Tcl_Panic("TclChanCaughtErrorBypass: Bad syntax of caught result"); 00912 } 00913 if (interp == NULL) { 00914 return; 00915 } 00916 00917 explicitResult = lc & 1; /* Odd number of values? */ 00918 numOptions = lc - explicitResult; 00919 00920 if (explicitResult) { 00921 Tcl_SetObjResult(interp, lv[lc-1]); 00922 } 00923 00924 (void) Tcl_SetReturnOptions(interp, Tcl_NewListObj(numOptions, lv)); 00925 ((Interp *)interp)->flags &= ~ERR_ALREADY_LOGGED; 00926 } 00927 00928 int 00929 TclChanCaughtErrorBypass( 00930 Tcl_Interp *interp, 00931 Tcl_Channel chan) 00932 { 00933 Tcl_Obj *chanMsgObj = NULL; 00934 Tcl_Obj *interpMsgObj = NULL; 00935 Tcl_Obj *msgObj = NULL; 00936 00937 /* 00938 * Get a bypassed error message from channel and/or interpreter, save the 00939 * reference, then kill the returned objects, if there were any. If there 00940 * are messages in both the channel has preference. 00941 */ 00942 00943 if ((chan == NULL) && (interp == NULL)) { 00944 return 0; 00945 } 00946 00947 if (chan != NULL) { 00948 Tcl_GetChannelError(chan, &chanMsgObj); 00949 } 00950 if (interp != NULL) { 00951 Tcl_GetChannelErrorInterp(interp, &interpMsgObj); 00952 } 00953 00954 if (chanMsgObj != NULL) { 00955 msgObj = chanMsgObj; 00956 } else if (interpMsgObj != NULL) { 00957 msgObj = interpMsgObj; 00958 } 00959 if (msgObj != NULL) { 00960 Tcl_IncrRefCount(msgObj); 00961 } 00962 00963 if (chanMsgObj != NULL) { 00964 Tcl_DecrRefCount(chanMsgObj); 00965 } 00966 if (interpMsgObj != NULL) { 00967 Tcl_DecrRefCount(interpMsgObj); 00968 } 00969 00970 /* 00971 * No message returned, nothing caught. 00972 */ 00973 00974 if (msgObj == NULL) { 00975 return 0; 00976 } 00977 00978 UnmarshallErrorResult(interp, msgObj); 00979 00980 Tcl_DecrRefCount(msgObj); 00981 return 1; 00982 } 00983 00984 /* 00985 * Driver functions. ================================================ 00986 */ 00987 00988 /* 00989 *---------------------------------------------------------------------- 00990 * 00991 * ReflectClose -- 00992 * 00993 * This function is invoked when the channel is closed, to delete the 00994 * driver specific instance data. 00995 * 00996 * Results: 00997 * A posix error. 00998 * 00999 * Side effects: 01000 * Releases memory. Arbitrary, as it calls upon a script. 01001 * 01002 *---------------------------------------------------------------------- 01003 */ 01004 01005 static int 01006 ReflectClose( 01007 ClientData clientData, 01008 Tcl_Interp *interp) 01009 { 01010 ReflectedChannel *rcPtr = (ReflectedChannel *) clientData; 01011 int result; /* Result code for 'close' */ 01012 Tcl_Obj *resObj; /* Result data for 'close' */ 01013 01014 if (interp == NULL) { 01015 /* 01016 * This call comes from TclFinalizeIOSystem. There are no 01017 * interpreters, and therefore we cannot call upon the handler command 01018 * anymore. Threading is irrelevant as well. We simply clean up all 01019 * our C level data structures and leave the Tcl level to the other 01020 * finalization functions. 01021 */ 01022 01023 /* 01024 * THREADED => Forward this to the origin thread 01025 * 01026 * Note: Have a thread delete handler for the origin thread. Use this 01027 * to clean up the structure! 01028 */ 01029 01030 #ifdef TCL_THREADS 01031 if (rcPtr->thread != Tcl_GetCurrentThread()) { 01032 ForwardParam p; 01033 01034 ForwardOpToOwnerThread(rcPtr, ForwardedClose, &p); 01035 result = p.base.code; 01036 01037 /* 01038 * FreeReflectedChannel is done in the forwarded operation!, in 01039 * the other thread. rcPtr here is gone! 01040 */ 01041 01042 if (result != TCL_OK) { 01043 FreeReceivedError(&p); 01044 } 01045 return EOK; 01046 } 01047 #endif 01048 01049 FreeReflectedChannel(rcPtr); 01050 return EOK; 01051 } 01052 01053 /* 01054 * -- No -- ASSERT rcPtr->methods & FLAG(METH_FINAL) 01055 * 01056 * A cleaned method mask here implies that the channel creation was 01057 * aborted, and "finalize" must not be called. 01058 */ 01059 01060 if (rcPtr->methods == 0) { 01061 FreeReflectedChannel(rcPtr); 01062 return EOK; 01063 } 01064 01065 /* 01066 * Are we in the correct thread? 01067 */ 01068 01069 #ifdef TCL_THREADS 01070 if (rcPtr->thread != Tcl_GetCurrentThread()) { 01071 ForwardParam p; 01072 01073 ForwardOpToOwnerThread(rcPtr, ForwardedClose, &p); 01074 result = p.base.code; 01075 01076 /* 01077 * FreeReflectedChannel is done in the forwarded operation!, in the 01078 * other thread. rcPtr here is gone! 01079 */ 01080 01081 if (result != TCL_OK) { 01082 PassReceivedErrorInterp(interp, &p); 01083 } 01084 } else { 01085 #endif 01086 result = InvokeTclMethod(rcPtr, "finalize", NULL, NULL, &resObj); 01087 if ((result != TCL_OK) && (interp != NULL)) { 01088 Tcl_SetChannelErrorInterp(interp, resObj); 01089 } 01090 01091 Tcl_DecrRefCount(resObj); /* Remove reference we held from the 01092 * invoke */ 01093 FreeReflectedChannel(rcPtr); 01094 #ifdef TCL_THREADS 01095 } 01096 #endif 01097 return (result == TCL_OK) ? EOK : EINVAL; 01098 } 01099 01100 /* 01101 *---------------------------------------------------------------------- 01102 * 01103 * ReflectInput -- 01104 * 01105 * This function is invoked when more data is requested from the channel. 01106 * 01107 * Results: 01108 * The number of bytes read. 01109 * 01110 * Side effects: 01111 * Allocates memory. Arbitrary, as it calls upon a script. 01112 * 01113 *---------------------------------------------------------------------- 01114 */ 01115 01116 static int 01117 ReflectInput( 01118 ClientData clientData, 01119 char *buf, 01120 int toRead, 01121 int *errorCodePtr) 01122 { 01123 ReflectedChannel *rcPtr = (ReflectedChannel *) clientData; 01124 Tcl_Obj *toReadObj; 01125 int bytec; /* Number of returned bytes */ 01126 unsigned char *bytev; /* Array of returned bytes */ 01127 Tcl_Obj *resObj; /* Result data for 'read' */ 01128 01129 /* 01130 * The following check can be done before thread redirection, because we 01131 * are reading from an item which is readonly, i.e. will never change 01132 * during the lifetime of the channel. 01133 */ 01134 01135 if (!(rcPtr->methods & FLAG(METH_READ))) { 01136 SetChannelErrorStr(rcPtr->chan, msg_read_unsup); 01137 *errorCodePtr = EINVAL; 01138 return -1; 01139 } 01140 01141 /* 01142 * Are we in the correct thread? 01143 */ 01144 01145 #ifdef TCL_THREADS 01146 if (rcPtr->thread != Tcl_GetCurrentThread()) { 01147 ForwardParam p; 01148 01149 p.input.buf = buf; 01150 p.input.toRead = toRead; 01151 01152 ForwardOpToOwnerThread(rcPtr, ForwardedInput, &p); 01153 01154 if (p.base.code != TCL_OK) { 01155 PassReceivedError(rcPtr->chan, &p); 01156 *errorCodePtr = EINVAL; 01157 } else { 01158 *errorCodePtr = EOK; 01159 } 01160 01161 return p.input.toRead; 01162 } 01163 #endif 01164 01165 /* ASSERT: rcPtr->method & FLAG(METH_READ) */ 01166 /* ASSERT: rcPtr->mode & TCL_READABLE */ 01167 01168 toReadObj = Tcl_NewIntObj(toRead); 01169 if (InvokeTclMethod(rcPtr, "read", toReadObj, NULL, &resObj)!=TCL_OK) { 01170 Tcl_SetChannelError(rcPtr->chan, resObj); 01171 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 01172 *errorCodePtr = EINVAL; 01173 return -1; 01174 } 01175 01176 bytev = Tcl_GetByteArrayFromObj(resObj, &bytec); 01177 01178 if (toRead < bytec) { 01179 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 01180 SetChannelErrorStr(rcPtr->chan, msg_read_toomuch); 01181 *errorCodePtr = EINVAL; 01182 return -1; 01183 } 01184 01185 *errorCodePtr = EOK; 01186 01187 if (bytec > 0) { 01188 memcpy(buf, bytev, (size_t)bytec); 01189 } 01190 01191 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 01192 return bytec; 01193 } 01194 01195 /* 01196 *---------------------------------------------------------------------- 01197 * 01198 * ReflectOutput -- 01199 * 01200 * This function is invoked when data is writen to the channel. 01201 * 01202 * Results: 01203 * The number of bytes actually written. 01204 * 01205 * Side effects: 01206 * Allocates memory. Arbitrary, as it calls upon a script. 01207 * 01208 *---------------------------------------------------------------------- 01209 */ 01210 01211 static int 01212 ReflectOutput( 01213 ClientData clientData, 01214 const char *buf, 01215 int toWrite, 01216 int *errorCodePtr) 01217 { 01218 ReflectedChannel *rcPtr = (ReflectedChannel *) clientData; 01219 Tcl_Obj *bufObj; 01220 Tcl_Obj *resObj; /* Result data for 'write' */ 01221 int written; 01222 01223 /* 01224 * The following check can be done before thread redirection, because we 01225 * are reading from an item which is readonly, i.e. will never change 01226 * during the lifetime of the channel. 01227 */ 01228 01229 if (!(rcPtr->methods & FLAG(METH_WRITE))) { 01230 SetChannelErrorStr(rcPtr->chan, msg_write_unsup); 01231 *errorCodePtr = EINVAL; 01232 return -1; 01233 } 01234 01235 /* 01236 * Are we in the correct thread? 01237 */ 01238 01239 #ifdef TCL_THREADS 01240 if (rcPtr->thread != Tcl_GetCurrentThread()) { 01241 ForwardParam p; 01242 01243 p.output.buf = buf; 01244 p.output.toWrite = toWrite; 01245 01246 ForwardOpToOwnerThread(rcPtr, ForwardedOutput, &p); 01247 01248 if (p.base.code != TCL_OK) { 01249 PassReceivedError(rcPtr->chan, &p); 01250 *errorCodePtr = EINVAL; 01251 } else { 01252 *errorCodePtr = EOK; 01253 } 01254 01255 return p.output.toWrite; 01256 } 01257 #endif 01258 01259 /* ASSERT: rcPtr->method & FLAG(METH_WRITE) */ 01260 /* ASSERT: rcPtr->mode & TCL_WRITABLE */ 01261 01262 bufObj = Tcl_NewByteArrayObj((unsigned char *) buf, toWrite); 01263 if (InvokeTclMethod(rcPtr, "write", bufObj, NULL, &resObj) != TCL_OK) { 01264 Tcl_SetChannelError(rcPtr->chan, resObj); 01265 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 01266 *errorCodePtr = EINVAL; 01267 return -1; 01268 } 01269 01270 if (Tcl_GetIntFromObj(rcPtr->interp, resObj, &written) != TCL_OK) { 01271 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 01272 Tcl_SetChannelError(rcPtr->chan, MarshallError(rcPtr->interp)); 01273 *errorCodePtr = EINVAL; 01274 return -1; 01275 } 01276 01277 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 01278 01279 if ((written == 0) || (toWrite < written)) { 01280 /* 01281 * The handler claims to have written more than it was given. That is 01282 * bad. Note that the I/O core would crash if we were to return this 01283 * information, trying to write -nnn bytes in the next iteration. 01284 */ 01285 01286 SetChannelErrorStr(rcPtr->chan, msg_write_toomuch); 01287 *errorCodePtr = EINVAL; 01288 return -1; 01289 } 01290 01291 *errorCodePtr = EOK; 01292 return written; 01293 } 01294 01295 /* 01296 *---------------------------------------------------------------------- 01297 * 01298 * ReflectSeekWide / ReflectSeek -- 01299 * 01300 * This function is invoked when the user wishes to seek on the channel. 01301 * 01302 * Results: 01303 * The new location of the access point. 01304 * 01305 * Side effects: 01306 * Allocates memory. Arbitrary, as it calls upon a script. 01307 * 01308 *---------------------------------------------------------------------- 01309 */ 01310 01311 static Tcl_WideInt 01312 ReflectSeekWide( 01313 ClientData clientData, 01314 Tcl_WideInt offset, 01315 int seekMode, 01316 int *errorCodePtr) 01317 { 01318 ReflectedChannel *rcPtr = (ReflectedChannel *) clientData; 01319 Tcl_Obj *offObj, *baseObj; 01320 Tcl_Obj *resObj; /* Result for 'seek' */ 01321 Tcl_WideInt newLoc; 01322 01323 /* 01324 * Are we in the correct thread? 01325 */ 01326 01327 #ifdef TCL_THREADS 01328 if (rcPtr->thread != Tcl_GetCurrentThread()) { 01329 ForwardParam p; 01330 01331 p.seek.seekMode = seekMode; 01332 p.seek.offset = offset; 01333 01334 ForwardOpToOwnerThread(rcPtr, ForwardedSeek, &p); 01335 01336 if (p.base.code != TCL_OK) { 01337 PassReceivedError(rcPtr->chan, &p); 01338 *errorCodePtr = EINVAL; 01339 } else { 01340 *errorCodePtr = EOK; 01341 } 01342 01343 return p.seek.offset; 01344 } 01345 #endif 01346 01347 /* ASSERT: rcPtr->method & FLAG(METH_SEEK) */ 01348 01349 offObj = Tcl_NewWideIntObj(offset); 01350 baseObj = Tcl_NewStringObj((seekMode == SEEK_SET) ? "start" : 01351 ((seekMode == SEEK_CUR) ? "current" : "end"), -1); 01352 if (InvokeTclMethod(rcPtr, "seek", offObj, baseObj, &resObj)!=TCL_OK) { 01353 Tcl_SetChannelError(rcPtr->chan, resObj); 01354 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 01355 *errorCodePtr = EINVAL; 01356 return -1; 01357 } 01358 01359 if (Tcl_GetWideIntFromObj(rcPtr->interp, resObj, &newLoc) != TCL_OK) { 01360 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 01361 Tcl_SetChannelError(rcPtr->chan, MarshallError(rcPtr->interp)); 01362 *errorCodePtr = EINVAL; 01363 return -1; 01364 } 01365 01366 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 01367 01368 if (newLoc < Tcl_LongAsWide(0)) { 01369 SetChannelErrorStr(rcPtr->chan, msg_seek_beforestart); 01370 *errorCodePtr = EINVAL; 01371 return -1; 01372 } 01373 01374 *errorCodePtr = EOK; 01375 return newLoc; 01376 } 01377 01378 static int 01379 ReflectSeek( 01380 ClientData clientData, 01381 long offset, 01382 int seekMode, 01383 int *errorCodePtr) 01384 { 01385 /* 01386 * This function can be invoked from a transformation which is based on 01387 * standard seeking, i.e. non-wide. Because of this we have to implement 01388 * it, a dummy is not enough. We simply delegate the call to the wide 01389 * routine. 01390 */ 01391 01392 return (int) ReflectSeekWide(clientData, Tcl_LongAsWide(offset), seekMode, 01393 errorCodePtr); 01394 } 01395 01396 /* 01397 *---------------------------------------------------------------------- 01398 * 01399 * ReflectWatch -- 01400 * 01401 * This function is invoked to tell the channel what events the I/O 01402 * system is interested in. 01403 * 01404 * Results: 01405 * None. 01406 * 01407 * Side effects: 01408 * Allocates memory. Arbitrary, as it calls upon a script. 01409 * 01410 *---------------------------------------------------------------------- 01411 */ 01412 01413 static void 01414 ReflectWatch( 01415 ClientData clientData, 01416 int mask) 01417 { 01418 ReflectedChannel *rcPtr = (ReflectedChannel *) clientData; 01419 Tcl_Obj *maskObj; 01420 01421 /* ASSERT rcPtr->methods & FLAG(METH_WATCH) */ 01422 01423 /* 01424 * We restrict the interest to what the channel can support. IOW there 01425 * will never be write events for a channel which is not writable. 01426 * Analoguously for read events and non-readable channels. 01427 */ 01428 01429 mask &= rcPtr->mode; 01430 01431 if (mask == rcPtr->interest) { 01432 /* 01433 * Same old, same old, why should we do something? 01434 */ 01435 01436 return; 01437 } 01438 01439 rcPtr->interest = mask; 01440 01441 /* 01442 * Are we in the correct thread? 01443 */ 01444 01445 #ifdef TCL_THREADS 01446 if (rcPtr->thread != Tcl_GetCurrentThread()) { 01447 ForwardParam p; 01448 01449 p.watch.mask = mask; 01450 ForwardOpToOwnerThread(rcPtr, ForwardedWatch, &p); 01451 01452 /* 01453 * Any failure from the forward is ignored. We have no place to put 01454 * this. 01455 */ 01456 01457 return; 01458 } 01459 #endif 01460 01461 maskObj = DecodeEventMask(mask); 01462 (void) InvokeTclMethod(rcPtr, "watch", maskObj, NULL, NULL); 01463 Tcl_DecrRefCount(maskObj); 01464 } 01465 01466 /* 01467 *---------------------------------------------------------------------- 01468 * 01469 * ReflectBlock -- 01470 * 01471 * This function is invoked to tell the channel which blocking behaviour 01472 * is required of it. 01473 * 01474 * Results: 01475 * A posix error number. 01476 * 01477 * Side effects: 01478 * Allocates memory. Arbitrary, as it calls upon a script. 01479 * 01480 *---------------------------------------------------------------------- 01481 */ 01482 01483 static int 01484 ReflectBlock( 01485 ClientData clientData, 01486 int nonblocking) 01487 { 01488 ReflectedChannel *rcPtr = (ReflectedChannel *) clientData; 01489 Tcl_Obj *blockObj; 01490 int errorNum; /* EINVAL or EOK (success). */ 01491 Tcl_Obj *resObj; /* Result data for 'blocking' */ 01492 01493 /* 01494 * Are we in the correct thread? 01495 */ 01496 01497 #ifdef TCL_THREADS 01498 if (rcPtr->thread != Tcl_GetCurrentThread()) { 01499 ForwardParam p; 01500 01501 p.block.nonblocking = nonblocking; 01502 01503 ForwardOpToOwnerThread(rcPtr, ForwardedBlock, &p); 01504 01505 if (p.base.code != TCL_OK) { 01506 PassReceivedError(rcPtr->chan, &p); 01507 return EINVAL; 01508 } 01509 01510 return EOK; 01511 } 01512 #endif 01513 01514 blockObj = Tcl_NewBooleanObj(!nonblocking); 01515 01516 if (InvokeTclMethod(rcPtr, "blocking", blockObj, NULL, &resObj) != TCL_OK) { 01517 Tcl_SetChannelError(rcPtr->chan, resObj); 01518 errorNum = EINVAL; 01519 } else { 01520 errorNum = EOK; 01521 } 01522 01523 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 01524 return errorNum; 01525 } 01526 01527 /* 01528 *---------------------------------------------------------------------- 01529 * 01530 * ReflectSetOption -- 01531 * 01532 * This function is invoked to configure a channel option. 01533 * 01534 * Results: 01535 * A standard Tcl result code. 01536 * 01537 * Side effects: 01538 * Arbitrary, as it calls upon a Tcl script. 01539 * 01540 *---------------------------------------------------------------------- 01541 */ 01542 01543 static int 01544 ReflectSetOption( 01545 ClientData clientData, /* Channel to query */ 01546 Tcl_Interp *interp, /* Interpreter to leave error messages in */ 01547 const char *optionName, /* Name of requested option */ 01548 const char *newValue) /* The new value */ 01549 { 01550 ReflectedChannel *rcPtr = (ReflectedChannel *) clientData; 01551 Tcl_Obj *optionObj, *valueObj; 01552 int result; /* Result code for 'configure' */ 01553 Tcl_Obj *resObj; /* Result data for 'configure' */ 01554 01555 /* 01556 * Are we in the correct thread? 01557 */ 01558 01559 #ifdef TCL_THREADS 01560 if (rcPtr->thread != Tcl_GetCurrentThread()) { 01561 ForwardParam p; 01562 01563 p.setOpt.name = optionName; 01564 p.setOpt.value = newValue; 01565 01566 ForwardOpToOwnerThread(rcPtr, ForwardedSetOpt, &p); 01567 01568 if (p.base.code != TCL_OK) { 01569 Tcl_Obj *err = Tcl_NewStringObj(p.base.msgStr, -1); 01570 01571 UnmarshallErrorResult(interp, err); 01572 Tcl_DecrRefCount(err); 01573 FreeReceivedError(&p); 01574 } 01575 01576 return p.base.code; 01577 } 01578 #endif 01579 01580 optionObj = Tcl_NewStringObj(optionName, -1); 01581 valueObj = Tcl_NewStringObj(newValue, -1); 01582 result = InvokeTclMethod(rcPtr, "configure",optionObj,valueObj, &resObj); 01583 if (result != TCL_OK) { 01584 UnmarshallErrorResult(interp, resObj); 01585 } 01586 01587 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 01588 return result; 01589 } 01590 01591 /* 01592 *---------------------------------------------------------------------- 01593 * 01594 * ReflectGetOption -- 01595 * 01596 * This function is invoked to retrieve all or a channel option. 01597 * 01598 * Results: 01599 * A standard Tcl result code. 01600 * 01601 * Side effects: 01602 * Arbitrary, as it calls upon a Tcl script. 01603 * 01604 *---------------------------------------------------------------------- 01605 */ 01606 01607 static int 01608 ReflectGetOption( 01609 ClientData clientData, /* Channel to query */ 01610 Tcl_Interp *interp, /* Interpreter to leave error messages in */ 01611 const char *optionName, /* Name of reuqested option */ 01612 Tcl_DString *dsPtr) /* String to place the result into */ 01613 { 01614 /* 01615 * This code is special. It has regular passing of Tcl result, and errors. 01616 * The bypass functions are not required. 01617 */ 01618 01619 ReflectedChannel *rcPtr = (ReflectedChannel*) clientData; 01620 Tcl_Obj *optionObj; 01621 Tcl_Obj *resObj; /* Result data for 'configure' */ 01622 int listc; 01623 Tcl_Obj **listv; 01624 const char *method; 01625 01626 /* 01627 * Are we in the correct thread? 01628 */ 01629 01630 #ifdef TCL_THREADS 01631 if (rcPtr->thread != Tcl_GetCurrentThread()) { 01632 int opcode; 01633 ForwardParam p; 01634 01635 p.getOpt.name = optionName; 01636 p.getOpt.value = dsPtr; 01637 01638 if (optionName == NULL) { 01639 opcode = ForwardedGetOptAll; 01640 } else { 01641 opcode = ForwardedGetOpt; 01642 } 01643 01644 ForwardOpToOwnerThread(rcPtr, opcode, &p); 01645 01646 if (p.base.code != TCL_OK) { 01647 Tcl_Obj *err = Tcl_NewStringObj(p.base.msgStr, -1); 01648 01649 UnmarshallErrorResult(interp, err); 01650 Tcl_DecrRefCount(err); 01651 FreeReceivedError(&p); 01652 } 01653 01654 return p.base.code; 01655 } 01656 #endif 01657 01658 if (optionName == NULL) { 01659 /* 01660 * Retrieve all options. 01661 */ 01662 01663 method = "cgetall"; 01664 optionObj = NULL; 01665 } else { 01666 /* 01667 * Retrieve the value of one option. 01668 */ 01669 01670 method = "cget"; 01671 optionObj = Tcl_NewStringObj(optionName, -1); 01672 } 01673 01674 if (InvokeTclMethod(rcPtr, method, optionObj, NULL, &resObj)!=TCL_OK) { 01675 UnmarshallErrorResult(interp, resObj); 01676 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 01677 return TCL_ERROR; 01678 } 01679 01680 /* 01681 * The result has to go into the 'dsPtr' for propagation to the caller of 01682 * the driver. 01683 */ 01684 01685 if (optionObj != NULL) { 01686 Tcl_DStringAppend(dsPtr, TclGetString(resObj), -1); 01687 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 01688 return TCL_OK; 01689 } 01690 01691 /* 01692 * Extract the list and append each item as element. 01693 */ 01694 01695 /* 01696 * NOTE (4): If we extract the string rep we can assume a properly quoted 01697 * string. Together with a separating space this way of simply appending 01698 * the whole string rep might be faster. It also doesn't check if the 01699 * result is a valid list. Nor that the list has an even number elements. 01700 */ 01701 01702 if (Tcl_ListObjGetElements(interp, resObj, &listc, &listv) != TCL_OK) { 01703 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 01704 return TCL_ERROR; 01705 } 01706 01707 if ((listc % 2) == 1) { 01708 /* 01709 * Odd number of elements is wrong. 01710 */ 01711 01712 Tcl_ResetResult(interp); 01713 Tcl_SetObjResult(interp, Tcl_ObjPrintf( 01714 "Expected list with even number of " 01715 "elements, got %d element%s instead", listc, 01716 (listc == 1 ? "" : "s"))); 01717 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 01718 return TCL_ERROR; 01719 } else { 01720 int len; 01721 char *str = Tcl_GetStringFromObj(resObj, &len); 01722 01723 if (len) { 01724 Tcl_DStringAppend(dsPtr, " ", 1); 01725 Tcl_DStringAppend(dsPtr, str, len); 01726 } 01727 Tcl_DecrRefCount(resObj); /* Remove reference held from invoke */ 01728 return TCL_OK; 01729 } 01730 } 01731 01732 /* 01733 * Helpers. ========================================================= 01734 */ 01735 01736 /* 01737 *---------------------------------------------------------------------- 01738 * 01739 * EncodeEventMask -- 01740 * 01741 * This function takes a list of event items and constructs the 01742 * equivalent internal bitmask. The list must contain at least one 01743 * element. Elements are "read", "write", or any unique abbreviation of 01744 * them. Note that the bitmask is not changed if problems are 01745 * encountered. 01746 * 01747 * Results: 01748 * A standard Tcl error code. A bitmask where TCL_READABLE and/or 01749 * TCL_WRITABLE can be set. 01750 * 01751 * Side effects: 01752 * May shimmer 'obj' to a list representation. May place an error message 01753 * into the interp result. 01754 * 01755 *---------------------------------------------------------------------- 01756 */ 01757 01758 static int 01759 EncodeEventMask( 01760 Tcl_Interp *interp, 01761 const char *objName, 01762 Tcl_Obj *obj, 01763 int *mask) 01764 { 01765 int events; /* Mask of events to post */ 01766 int listc; /* #elements in eventspec list */ 01767 Tcl_Obj **listv; /* Elements of eventspec list */ 01768 int evIndex; /* Id of event for an element of the eventspec 01769 * list. */ 01770 01771 if (Tcl_ListObjGetElements(interp, obj, &listc, &listv) != TCL_OK) { 01772 return TCL_ERROR; 01773 } 01774 01775 if (listc < 1) { 01776 Tcl_AppendResult(interp, "bad ", objName, " list: is empty", NULL); 01777 return TCL_ERROR; 01778 } 01779 01780 events = 0; 01781 while (listc > 0) { 01782 if (Tcl_GetIndexFromObj(interp, listv[listc-1], eventOptions, 01783 objName, 0, &evIndex) != TCL_OK) { 01784 return TCL_ERROR; 01785 } 01786 switch (evIndex) { 01787 case EVENT_READ: 01788 events |= TCL_READABLE; 01789 break; 01790 case EVENT_WRITE: 01791 events |= TCL_WRITABLE; 01792 break; 01793 } 01794 listc --; 01795 } 01796 01797 *mask = events; 01798 return TCL_OK; 01799 } 01800 01801 /* 01802 *---------------------------------------------------------------------- 01803 * 01804 * DecodeEventMask -- 01805 * 01806 * This function takes an internal bitmask of events and constructs the 01807 * equivalent list of event items. 01808 * 01809 * Results: 01810 * A Tcl_Obj reference. The object will have a refCount of one. The user 01811 * has to decrement it to release the object. 01812 * 01813 * Side effects: 01814 * None. 01815 * 01816 *---------------------------------------------------------------------- 01817 */ 01818 01819 static Tcl_Obj * 01820 DecodeEventMask( 01821 int mask) 01822 { 01823 register const char *eventStr; 01824 Tcl_Obj *evObj; 01825 01826 switch (mask & RANDW) { 01827 case RANDW: 01828 eventStr = "read write"; 01829 break; 01830 case TCL_READABLE: 01831 eventStr = "read"; 01832 break; 01833 case TCL_WRITABLE: 01834 eventStr = "write"; 01835 break; 01836 default: 01837 eventStr = ""; 01838 break; 01839 } 01840 01841 evObj = Tcl_NewStringObj(eventStr, -1); 01842 Tcl_IncrRefCount(evObj); 01843 return evObj; 01844 } 01845 01846 /* 01847 *---------------------------------------------------------------------- 01848 * 01849 * NewReflectedChannel -- 01850 * 01851 * This function is invoked to allocate and initialize the instance data 01852 * of a new reflected channel. 01853 * 01854 * Results: 01855 * A heap-allocated channel instance. 01856 * 01857 * Side effects: 01858 * Allocates memory. 01859 * 01860 *---------------------------------------------------------------------- 01861 */ 01862 01863 static ReflectedChannel * 01864 NewReflectedChannel( 01865 Tcl_Interp *interp, 01866 Tcl_Obj *cmdpfxObj, 01867 int mode, 01868 Tcl_Obj *handleObj) 01869 { 01870 ReflectedChannel *rcPtr; 01871 int i, listc; 01872 Tcl_Obj **listv; 01873 01874 rcPtr = (ReflectedChannel *) ckalloc(sizeof(ReflectedChannel)); 01875 01876 /* rcPtr->chan: Assigned by caller. Dummy data here. */ 01877 /* rcPtr->methods: Assigned by caller. Dummy data here. */ 01878 01879 rcPtr->chan = NULL; 01880 rcPtr->methods = 0; 01881 rcPtr->interp = interp; 01882 #ifdef TCL_THREADS 01883 rcPtr->thread = Tcl_GetCurrentThread(); 01884 #endif 01885 rcPtr->mode = mode; 01886 rcPtr->interest = 0; /* Initially no interest registered */ 01887 01888 /* 01889 * Method placeholder. 01890 */ 01891 01892 /* ASSERT: cmdpfxObj is a Tcl List */ 01893 01894 Tcl_ListObjGetElements(interp, cmdpfxObj, &listc, &listv); 01895 01896 /* 01897 * See [==] as well. 01898 * Storage for the command prefix and the additional words required for 01899 * the invocation of methods in the command handler. 01900 * 01901 * listv [0] [listc-1] | [listc] [listc+1] | 01902 * argv [0] ... [.] | [argc-2] [argc-1] | [argc] [argc+2] 01903 * cmd ... pfx | method chan | detail1 detail2 01904 */ 01905 01906 rcPtr->argc = listc + 2; 01907 rcPtr->argv = (Tcl_Obj **) ckalloc(sizeof(Tcl_Obj *) * (listc+4)); 01908 01909 /* 01910 * Duplicate object references. 01911 */ 01912 01913 for (i=0; i<listc ; i++) { 01914 Tcl_Obj *word = rcPtr->argv[i] = listv[i]; 01915 01916 Tcl_IncrRefCount(word); 01917 } 01918 01919 i++; /* Skip placeholder for method */ 01920 01921 /* 01922 * [Bug 1667990]: See [x] in FreeReflectedChannel for release 01923 */ 01924 01925 rcPtr->argv[i] = handleObj; 01926 Tcl_IncrRefCount(handleObj); 01927 01928 /* 01929 * The next two objects are kept empty, varying arguments. 01930 */ 01931 01932 /* 01933 * Initialization complete. 01934 */ 01935 01936 return rcPtr; 01937 } 01938 01939 /* 01940 *---------------------------------------------------------------------- 01941 * 01942 * NextHandle -- 01943 * 01944 * This function is invoked to generate a channel handle for a new 01945 * reflected channel. 01946 * 01947 * Results: 01948 * A Tcl_Obj containing the string of the new channel handle. The 01949 * refcount of the returned object is -- zero --. 01950 * 01951 * Side effects: 01952 * May allocate memory. Mutex protected critical section locks out other 01953 * threads for a short time. 01954 * 01955 *---------------------------------------------------------------------- 01956 */ 01957 01958 static Tcl_Obj * 01959 NextHandle(void) 01960 { 01961 /* 01962 * Count number of generated reflected channels. Used for id generation. 01963 * Ids are never reclaimed and there is no dealing with wrap around. On 01964 * the other hand, "unsigned long" should be big enough except for 01965 * absolute longrunners (generate a 100 ids per second => overflow will 01966 * occur in 1 1/3 years). 01967 */ 01968 01969 TCL_DECLARE_MUTEX(rcCounterMutex) 01970 static unsigned long rcCounter = 0; 01971 Tcl_Obj *resObj; 01972 01973 Tcl_MutexLock(&rcCounterMutex); 01974 resObj = Tcl_ObjPrintf("rc%lu", rcCounter); 01975 rcCounter++; 01976 Tcl_MutexUnlock(&rcCounterMutex); 01977 01978 return resObj; 01979 } 01980 01981 static void 01982 FreeReflectedChannel( 01983 ReflectedChannel *rcPtr) 01984 { 01985 Channel *chanPtr = (Channel *) rcPtr->chan; 01986 int i, n; 01987 01988 if (chanPtr->typePtr != &tclRChannelType) { 01989 /* 01990 * Delete a cloned ChannelType structure. 01991 */ 01992 01993 ckfree((char*) chanPtr->typePtr); 01994 } 01995 01996 n = rcPtr->argc - 2; 01997 for (i=0; i<n; i++) { 01998 Tcl_DecrRefCount(rcPtr->argv[i]); 01999 } 02000 02001 /* 02002 * [Bug 1667990]: See [x] in NewReflectedChannel for lock. n+1 = argc-1. 02003 */ 02004 02005 Tcl_DecrRefCount(rcPtr->argv[n+1]); 02006 02007 ckfree((char*) rcPtr->argv); 02008 ckfree((char*) rcPtr); 02009 } 02010 02011 /* 02012 *---------------------------------------------------------------------- 02013 * 02014 * InvokeTclMethod -- 02015 * 02016 * This function is used to invoke the Tcl level of a reflected channel. 02017 * It handles all the command assembly, invokation, and generic state and 02018 * result mgmt. It does *not* handle thread redirection; that is the 02019 * responsibility of clients of this function. 02020 * 02021 * Results: 02022 * Result code and data as returned by the method. 02023 * 02024 * Side effects: 02025 * Arbitrary, as it calls upon a Tcl script. 02026 * 02027 *---------------------------------------------------------------------- 02028 */ 02029 02030 static int 02031 InvokeTclMethod( 02032 ReflectedChannel *rcPtr, 02033 const char *method, 02034 Tcl_Obj *argOneObj, /* NULL'able */ 02035 Tcl_Obj *argTwoObj, /* NULL'able */ 02036 Tcl_Obj **resultObjPtr) /* NULL'able */ 02037 { 02038 int cmdc; /* #words in constructed command */ 02039 Tcl_Obj *methObj = NULL; /* Method name in object form */ 02040 Tcl_InterpState sr; /* State of handler interp */ 02041 int result; /* Result code of method invokation */ 02042 Tcl_Obj *resObj = NULL; /* Result of method invokation. */ 02043 02044 /* 02045 * NOTE (5): Decide impl. issue: Cache objects with method names? Needs 02046 * TSD data as reflections can be created in many different threads. 02047 */ 02048 02049 /* 02050 * Insert method into the pre-allocated area, after the command prefix, 02051 * before the channel id. 02052 */ 02053 02054 methObj = Tcl_NewStringObj(method, -1); 02055 Tcl_IncrRefCount(methObj); 02056 rcPtr->argv[rcPtr->argc - 2] = methObj; 02057 02058 /* 02059 * Append the additional argument containing method specific details 02060 * behind the channel id. If specified. 02061 */ 02062 02063 cmdc = rcPtr->argc; 02064 if (argOneObj) { 02065 Tcl_IncrRefCount(argOneObj); 02066 rcPtr->argv[cmdc] = argOneObj; 02067 cmdc++; 02068 if (argTwoObj) { 02069 Tcl_IncrRefCount(argTwoObj); 02070 rcPtr->argv[cmdc] = argTwoObj; 02071 cmdc++; 02072 } 02073 } 02074 02075 /* 02076 * And run the handler... This is done in auch a manner which leaves any 02077 * existing state intact. 02078 */ 02079 02080 sr = Tcl_SaveInterpState(rcPtr->interp, 0 /* Dummy */); 02081 Tcl_Preserve(rcPtr->interp); 02082 result = Tcl_EvalObjv(rcPtr->interp, cmdc, rcPtr->argv, TCL_EVAL_GLOBAL); 02083 02084 /* 02085 * We do not try to extract the result information if the caller has no 02086 * interest in it. I.e. there is no need to put effort into creating 02087 * something which is discarded immediately after. 02088 */ 02089 02090 if (resultObjPtr) { 02091 if (result == TCL_OK) { 02092 /* 02093 * Ok result taken as is, also if the caller requests that there 02094 * is no capture. 02095 */ 02096 02097 resObj = Tcl_GetObjResult(rcPtr->interp); 02098 } else { 02099 /* 02100 * Non-ok result is always treated as an error. We have to capture 02101 * the full state of the result, including additional options. 02102 * 02103 * This is complex and ugly, and would be completely unnecessary 02104 * if we only added support for a TCL_FORBID_EXCEPTIONS flag. 02105 */ 02106 02107 if (result != TCL_ERROR) { 02108 Tcl_Obj *cmd = Tcl_NewListObj(cmdc, rcPtr->argv); 02109 int cmdLen; 02110 const char *cmdString = Tcl_GetStringFromObj(cmd, &cmdLen); 02111 02112 Tcl_IncrRefCount(cmd); 02113 Tcl_ResetResult(rcPtr->interp); 02114 Tcl_SetObjResult(rcPtr->interp, Tcl_ObjPrintf( 02115 "chan handler returned bad code: %d", result)); 02116 Tcl_LogCommandInfo(rcPtr->interp, cmdString, cmdString, 02117 cmdLen); 02118 Tcl_DecrRefCount(cmd); 02119 result = TCL_ERROR; 02120 } 02121 Tcl_AppendObjToErrorInfo(rcPtr->interp, Tcl_ObjPrintf( 02122 "\n (chan handler subcommand \"%s\")", method)); 02123 resObj = MarshallError(rcPtr->interp); 02124 } 02125 Tcl_IncrRefCount(resObj); 02126 } 02127 Tcl_RestoreInterpState(rcPtr->interp, sr); 02128 Tcl_Release(rcPtr->interp); 02129 02130 /* 02131 * Cleanup of the dynamic parts of the command. 02132 */ 02133 02134 Tcl_DecrRefCount(methObj); 02135 if (argOneObj) { 02136 Tcl_DecrRefCount(argOneObj); 02137 if (argTwoObj) { 02138 Tcl_DecrRefCount(argTwoObj); 02139 } 02140 } 02141 02142 /* 02143 * The resObj has a ref count of 1 at this location. This means that the 02144 * caller of InvokeTclMethod has to dispose of it (but only if it was 02145 * returned to it). 02146 */ 02147 02148 if (resultObjPtr != NULL) { 02149 *resultObjPtr = resObj; 02150 } 02151 02152 /* 02153 * There no need to handle the case where nothing is returned, because for 02154 * that case resObj was not set anyway. 02155 */ 02156 02157 return result; 02158 } 02159 02160 /* 02161 *---------------------------------------------------------------------- 02162 * 02163 * GetReflectedChannelMap -- 02164 * 02165 * Gets and potentially initializes the reflected channel map for an 02166 * interpreter. 02167 * 02168 * Results: 02169 * A pointer to the map created, for use by the caller. 02170 * 02171 * Side effects: 02172 * Initializes the reflected channel map for an interpreter. 02173 * 02174 *---------------------------------------------------------------------- 02175 */ 02176 02177 static ReflectedChannelMap * 02178 GetReflectedChannelMap( 02179 Tcl_Interp *interp) 02180 { 02181 ReflectedChannelMap* rcmPtr = Tcl_GetAssocData(interp, RCMKEY, NULL); 02182 02183 if (rcmPtr == NULL) { 02184 rcmPtr = (ReflectedChannelMap *) ckalloc(sizeof(ReflectedChannelMap)); 02185 Tcl_InitHashTable(&rcmPtr->map, TCL_STRING_KEYS); 02186 Tcl_SetAssocData(interp, RCMKEY, 02187 (Tcl_InterpDeleteProc *) DeleteReflectedChannelMap, rcmPtr); 02188 } 02189 return rcmPtr; 02190 } 02191 02192 /* 02193 *---------------------------------------------------------------------- 02194 * 02195 * DeleteReflectedChannelMap -- 02196 * 02197 * Deletes the channel table for an interpreter, closing any open 02198 * channels whose refcount reaches zero. This procedure is invoked when 02199 * an interpreter is deleted, via the AssocData cleanup mechanism. 02200 * 02201 * Results: 02202 * None. 02203 * 02204 * Side effects: 02205 * Deletes the hash table of channels. May close channels. May flush 02206 * output on closed channels. Removes any channeEvent handlers that were 02207 * registered in this interpreter. 02208 * 02209 *---------------------------------------------------------------------- 02210 */ 02211 02212 static void 02213 DeleteReflectedChannelMap( 02214 ClientData clientData, /* The per-interpreter data structure. */ 02215 Tcl_Interp *interp) /* The interpreter being deleted. */ 02216 { 02217 ReflectedChannelMap* rcmPtr; /* The map */ 02218 Tcl_HashSearch hSearch; /* Search variable. */ 02219 Tcl_HashEntry *hPtr; /* Search variable. */ 02220 02221 /* 02222 * Delete all entries. The channels may have been closed alreay, or will 02223 * be closed later, by the standard IO finalization of an interpreter 02224 * under destruction. 02225 */ 02226 02227 rcmPtr = clientData; 02228 for (hPtr = Tcl_FirstHashEntry(&rcmPtr->map, &hSearch); 02229 hPtr != NULL; 02230 hPtr = Tcl_FirstHashEntry(&rcmPtr->map, &hSearch)) { 02231 02232 Tcl_DeleteHashEntry(hPtr); 02233 } 02234 Tcl_DeleteHashTable(&rcmPtr->map); 02235 ckfree((char *) &rcmPtr->map); 02236 } 02237 02238 #ifdef TCL_THREADS 02239 static void 02240 ForwardOpToOwnerThread( 02241 ReflectedChannel *rcPtr, /* Channel instance */ 02242 ForwardedOperation op, /* Forwarded driver operation */ 02243 const VOID *param) /* Arguments */ 02244 { 02245 Tcl_ThreadId dst = rcPtr->thread; 02246 ForwardingEvent *evPtr; 02247 ForwardingResult *resultPtr; 02248 int result; 02249 02250 /* 02251 * Create and initialize the event and data structures. 02252 */ 02253 02254 evPtr = (ForwardingEvent *) ckalloc(sizeof(ForwardingEvent)); 02255 resultPtr = (ForwardingResult *) ckalloc(sizeof(ForwardingResult)); 02256 02257 evPtr->event.proc = ForwardProc; 02258 evPtr->resultPtr = resultPtr; 02259 evPtr->op = op; 02260 evPtr->rcPtr = rcPtr; 02261 evPtr->param = (ForwardParam *) param; 02262 02263 resultPtr->src = Tcl_GetCurrentThread(); 02264 resultPtr->dst = dst; 02265 resultPtr->done = NULL; 02266 resultPtr->result = -1; 02267 resultPtr->evPtr = evPtr; 02268 02269 /* 02270 * Now execute the forward. 02271 */ 02272 02273 Tcl_MutexLock(&rcForwardMutex); 02274 TclSpliceIn(resultPtr, forwardList); 02275 02276 /* 02277 * Ensure cleanup of the event if any of the two involved threads exits 02278 * while this event is pending or in progress. 02279 */ 02280 02281 Tcl_CreateThreadExitHandler(SrcExitProc, (ClientData) evPtr); 02282 Tcl_CreateThreadExitHandler(DstExitProc, (ClientData) evPtr); 02283 02284 /* 02285 * Queue the event and poke the other thread's notifier. 02286 */ 02287 02288 Tcl_ThreadQueueEvent(dst, (Tcl_Event *)evPtr, TCL_QUEUE_TAIL); 02289 Tcl_ThreadAlert(dst); 02290 02291 /* 02292 * (*) Block until the other thread has either processed the transfer or 02293 * rejected it. 02294 */ 02295 02296 while (resultPtr->result < 0) { 02297 /* 02298 * NOTE (1): Is it possible that the current thread goes away while 02299 * waiting here? IOW Is it possible that "SrcExitProc" is called while 02300 * we are here? See complementary note (2) in "SrcExitProc" 02301 */ 02302 02303 Tcl_ConditionWait(&resultPtr->done, &rcForwardMutex, NULL); 02304 } 02305 02306 /* 02307 * Unlink result from the forwarder list. 02308 */ 02309 02310 TclSpliceOut(resultPtr, forwardList); 02311 02312 resultPtr->nextPtr = NULL; 02313 resultPtr->prevPtr = NULL; 02314 02315 Tcl_MutexUnlock(&rcForwardMutex); 02316 Tcl_ConditionFinalize(&resultPtr->done); 02317 02318 /* 02319 * Kill the cleanup handlers now, and the result structure as well, before 02320 * returning the success code. 02321 * 02322 * Note: The event structure has already been deleted. 02323 */ 02324 02325 Tcl_DeleteThreadExitHandler(SrcExitProc, (ClientData) evPtr); 02326 Tcl_DeleteThreadExitHandler(DstExitProc, (ClientData) evPtr); 02327 02328 result = resultPtr->result; 02329 ckfree((char*) resultPtr); 02330 } 02331 02332 static int 02333 ForwardProc( 02334 Tcl_Event *evGPtr, 02335 int mask) 02336 { 02337 /* 02338 * Notes regarding access to the referenced data. 02339 * 02340 * In principle the data belongs to the originating thread (see 02341 * evPtr->src), however this thread is currently blocked at (*), i.e. 02342 * quiescent. Because of this we can treat the data as belonging to us, 02343 * without fear of race conditions. I.e. we can read and write as we like. 02344 * 02345 * The only thing we cannot be sure of is the resultPtr. This can be be 02346 * NULLed if the originating thread went away while the event is handled 02347 * here now. 02348 */ 02349 02350 ForwardingEvent *evPtr = (ForwardingEvent *) evGPtr; 02351 ForwardingResult *resultPtr = evPtr->resultPtr; 02352 ReflectedChannel *rcPtr = evPtr->rcPtr; 02353 Tcl_Interp *interp = rcPtr->interp; 02354 ForwardParam *paramPtr = evPtr->param; 02355 Tcl_Obj *resObj = NULL; /* Interp result of InvokeTclMethod */ 02356 02357 /* 02358 * Ignore the event if no one is waiting for its result anymore. 02359 */ 02360 02361 if (!resultPtr) { 02362 return 1; 02363 } 02364 02365 paramPtr->base.code = TCL_OK; 02366 paramPtr->base.msgStr = NULL; 02367 paramPtr->base.mustFree = 0; 02368 02369 switch (evPtr->op) { 02370 /* 02371 * The destination thread for the following operations is 02372 * rcPtr->thread, which contains rcPtr->interp, the interp we have to 02373 * call upon for the driver. 02374 */ 02375 02376 case ForwardedClose: 02377 /* 02378 * No parameters/results. 02379 */ 02380 02381 if (InvokeTclMethod(rcPtr, "finalize", NULL, NULL, &resObj)!=TCL_OK) { 02382 ForwardSetObjError(paramPtr, resObj); 02383 } 02384 02385 /* 02386 * Freeing is done here, in the origin thread, because the argv[] 02387 * objects belong to this thread. Deallocating them in a different 02388 * thread is not allowed 02389 */ 02390 02391 FreeReflectedChannel(rcPtr); 02392 break; 02393 02394 case ForwardedInput: { 02395 Tcl_Obj *toReadObj = Tcl_NewIntObj(paramPtr->input.toRead); 02396 02397 if (InvokeTclMethod(rcPtr, "read", toReadObj, NULL, &resObj)!=TCL_OK){ 02398 ForwardSetObjError(paramPtr, resObj); 02399 paramPtr->input.toRead = -1; 02400 } else { 02401 /* 02402 * Process a regular result. 02403 */ 02404 02405 int bytec; /* Number of returned bytes */ 02406 unsigned char *bytev; /* Array of returned bytes */ 02407 02408 bytev = Tcl_GetByteArrayFromObj(resObj, &bytec); 02409 02410 if (paramPtr->input.toRead < bytec) { 02411 ForwardSetStaticError(paramPtr, msg_read_toomuch); 02412 paramPtr->input.toRead = -1; 02413 } else { 02414 if (bytec > 0) { 02415 memcpy(paramPtr->input.buf, bytev, (size_t)bytec); 02416 } 02417 paramPtr->input.toRead = bytec; 02418 } 02419 } 02420 break; 02421 } 02422 02423 case ForwardedOutput: { 02424 Tcl_Obj *bufObj = Tcl_NewByteArrayObj((unsigned char *) 02425 paramPtr->output.buf, paramPtr->output.toWrite); 02426 02427 if (InvokeTclMethod(rcPtr, "write", bufObj, NULL, &resObj) != TCL_OK) { 02428 ForwardSetObjError(paramPtr, resObj); 02429 paramPtr->output.toWrite = -1; 02430 } else { 02431 /* 02432 * Process a regular result. 02433 */ 02434 02435 int written; 02436 02437 if (Tcl_GetIntFromObj(interp, resObj, &written) != TCL_OK) { 02438 ForwardSetObjError(paramPtr, MarshallError(interp)); 02439 paramPtr->output.toWrite = -1; 02440 } else if (written==0 || paramPtr->output.toWrite<written) { 02441 ForwardSetStaticError(paramPtr, msg_write_toomuch); 02442 paramPtr->output.toWrite = -1; 02443 } else { 02444 paramPtr->output.toWrite = written; 02445 } 02446 } 02447 break; 02448 } 02449 02450 case ForwardedSeek: { 02451 Tcl_Obj *offObj = Tcl_NewWideIntObj(paramPtr->seek.offset); 02452 Tcl_Obj *baseObj = Tcl_NewStringObj( 02453 (paramPtr->seek.seekMode==SEEK_SET) ? "start" : 02454 (paramPtr->seek.seekMode==SEEK_CUR) ? "current" : "end", -1); 02455 02456 if (InvokeTclMethod(rcPtr, "seek", offObj, baseObj, &resObj)!=TCL_OK){ 02457 ForwardSetObjError(paramPtr, resObj); 02458 paramPtr->seek.offset = -1; 02459 } else { 02460 /* 02461 * Process a regular result. If the type is wrong this may change 02462 * into an error. 02463 */ 02464 02465 Tcl_WideInt newLoc; 02466 02467 if (Tcl_GetWideIntFromObj(interp, resObj, &newLoc) == TCL_OK) { 02468 if (newLoc < Tcl_LongAsWide(0)) { 02469 ForwardSetStaticError(paramPtr, msg_seek_beforestart); 02470 paramPtr->seek.offset = -1; 02471 } else { 02472 paramPtr->seek.offset = newLoc; 02473 } 02474 } else { 02475 ForwardSetObjError(paramPtr, MarshallError(interp)); 02476 paramPtr->seek.offset = -1; 02477 } 02478 } 02479 break; 02480 } 02481 02482 case ForwardedWatch: { 02483 Tcl_Obj *maskObj = DecodeEventMask(paramPtr->watch.mask); 02484 02485 (void) InvokeTclMethod(rcPtr, "watch", maskObj, NULL, NULL); 02486 Tcl_DecrRefCount(maskObj); 02487 break; 02488 } 02489 02490 case ForwardedBlock: { 02491 Tcl_Obj *blockObj = Tcl_NewBooleanObj(!paramPtr->block.nonblocking); 02492 02493 if (InvokeTclMethod(rcPtr, "blocking", blockObj, NULL, 02494 &resObj) != TCL_OK) { 02495 ForwardSetObjError(paramPtr, resObj); 02496 } 02497 break; 02498 } 02499 02500 case ForwardedSetOpt: { 02501 Tcl_Obj *optionObj = Tcl_NewStringObj(paramPtr->setOpt.name, -1); 02502 Tcl_Obj *valueObj = Tcl_NewStringObj(paramPtr->setOpt.value, -1); 02503 02504 if (InvokeTclMethod(rcPtr, "configure", optionObj, valueObj, 02505 &resObj) != TCL_OK) { 02506 ForwardSetObjError(paramPtr, resObj); 02507 } 02508 break; 02509 } 02510 02511 case ForwardedGetOpt: { 02512 /* 02513 * Retrieve the value of one option. 02514 */ 02515 02516 Tcl_Obj *optionObj = Tcl_NewStringObj(paramPtr->getOpt.name, -1); 02517 02518 if (InvokeTclMethod(rcPtr, "cget", optionObj, NULL, &resObj)!=TCL_OK){ 02519 ForwardSetObjError(paramPtr, resObj); 02520 } else { 02521 Tcl_DStringAppend(paramPtr->getOpt.value, 02522 TclGetString(resObj), -1); 02523 } 02524 break; 02525 } 02526 02527 case ForwardedGetOptAll: 02528 /* 02529 * Retrieve all options. 02530 */ 02531 02532 if (InvokeTclMethod(rcPtr, "cgetall", NULL, NULL, &resObj) != TCL_OK){ 02533 ForwardSetObjError(paramPtr, resObj); 02534 } else { 02535 /* 02536 * Extract list, validate that it is a list, and #elements. See 02537 * NOTE (4) as well. 02538 */ 02539 02540 int listc; 02541 Tcl_Obj **listv; 02542 02543 if (Tcl_ListObjGetElements(interp, resObj, &listc, 02544 &listv) != TCL_OK) { 02545 ForwardSetObjError(paramPtr, MarshallError(interp)); 02546 } else if ((listc % 2) == 1) { 02547 /* 02548 * Odd number of elements is wrong. [x]. 02549 */ 02550 02551 char *buf = ckalloc(200); 02552 sprintf(buf, 02553 "{Expected list with even number of elements, got %d %s instead}", 02554 listc, (listc == 1 ? "element" : "elements")); 02555 02556 ForwardSetDynamicError(paramPtr, buf); 02557 } else { 02558 int len; 02559 const char *str = Tcl_GetStringFromObj(resObj, &len); 02560 02561 if (len) { 02562 Tcl_DStringAppend(paramPtr->getOpt.value, " ", 1); 02563 Tcl_DStringAppend(paramPtr->getOpt.value, str, len); 02564 } 02565 } 02566 } 02567 break; 02568 02569 default: 02570 /* 02571 * Bad operation code. 02572 */ 02573 02574 Tcl_Panic("Bad operation code in ForwardProc"); 02575 break; 02576 } 02577 02578 /* 02579 * Remove the reference we held on the result of the invoke, if we had 02580 * such. 02581 */ 02582 02583 if (resObj != NULL) { 02584 Tcl_DecrRefCount(resObj); 02585 } 02586 02587 if (resultPtr) { 02588 /* 02589 * Report the forwarding result synchronously to the waiting caller. 02590 * This unblocks (*) as well. This is wrapped into a conditional 02591 * because the caller may have exited in the mean time. 02592 */ 02593 02594 Tcl_MutexLock(&rcForwardMutex); 02595 resultPtr->result = TCL_OK; 02596 Tcl_ConditionNotify(&resultPtr->done); 02597 Tcl_MutexUnlock(&rcForwardMutex); 02598 } 02599 02600 return 1; 02601 } 02602 02603 static void 02604 SrcExitProc( 02605 ClientData clientData) 02606 { 02607 ForwardingEvent *evPtr = (ForwardingEvent *) clientData; 02608 ForwardingResult *resultPtr; 02609 ForwardParam *paramPtr; 02610 02611 /* 02612 * NOTE (2): Can this handler be called with the originator blocked? 02613 */ 02614 02615 /* 02616 * The originator for the event exited. It is not sure if this can happen, 02617 * as the originator should be blocked at (*) while the event is in 02618 * transit/pending. 02619 * 02620 * We make sure that the event cannot refer to the result anymore, remove 02621 * it from the list of pending results and free the structure. Locking the 02622 * access ensures that we cannot get in conflict with "ForwardProc", 02623 * should it already execute the event. 02624 */ 02625 02626 Tcl_MutexLock(&rcForwardMutex); 02627 02628 resultPtr = evPtr->resultPtr; 02629 paramPtr = evPtr->param; 02630 02631 evPtr->resultPtr = NULL; 02632 resultPtr->evPtr = NULL; 02633 resultPtr->result = TCL_ERROR; 02634 02635 ForwardSetStaticError(paramPtr, msg_send_originlost); 02636 02637 /* 02638 * See below: TclSpliceOut(resultPtr, forwardList); 02639 */ 02640 02641 Tcl_MutexUnlock(&rcForwardMutex); 02642 02643 /* 02644 * This unlocks (*). The structure will be spliced out and freed by 02645 * "ForwardProc". Maybe. 02646 */ 02647 02648 Tcl_ConditionNotify(&resultPtr->done); 02649 } 02650 02651 static void 02652 DstExitProc( 02653 ClientData clientData) 02654 { 02655 ForwardingEvent *evPtr = (ForwardingEvent *) clientData; 02656 ForwardingResult *resultPtr = evPtr->resultPtr; 02657 ForwardParam *paramPtr = evPtr->param; 02658 02659 /* 02660 * NOTE (3): It is not clear if the event still exists when this handler 02661 * is called. We might have to use 'resultPtr' as our clientData instead. 02662 */ 02663 02664 /* 02665 * The receiver for the event exited, before processing the event. We 02666 * detach the result now, wake the originator up and signal failure. 02667 */ 02668 02669 evPtr->resultPtr = NULL; 02670 resultPtr->evPtr = NULL; 02671 resultPtr->result = TCL_ERROR; 02672 02673 ForwardSetStaticError(paramPtr, msg_send_dstlost); 02674 02675 Tcl_ConditionNotify(&resultPtr->done); 02676 } 02677 02678 static void 02679 ForwardSetObjError( 02680 ForwardParam *paramPtr, 02681 Tcl_Obj *obj) 02682 { 02683 int len; 02684 const char *msgStr = Tcl_GetStringFromObj(obj, &len); 02685 02686 len++; 02687 ForwardSetDynamicError(paramPtr, ckalloc((unsigned) len)); 02688 memcpy(paramPtr->base.msgStr, msgStr, (unsigned) len); 02689 } 02690 #endif 02691 02692 /* 02693 * Local Variables: 02694 * mode: c 02695 * c-basic-offset: 4 02696 * fill-column: 78 02697 * End: 02698 */
Generated on Wed Mar 12 12:18:18 2008 by 1.5.1 |