Tcl Source Code

Check-in [9dc4acda9c]
Login
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2019 Conference, Houston/TX, US, Nov 4-8
Send your abstracts to [email protected]
or submit via the online form by Sep 9.

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:* generic/tclMain.c (Tcl_Main): [Bug 801429]: Factor out the holding of the client-installed main loop function into thread-specific data.

***POTENTIAL INCOMPATIBILITY*** Code that previously tried to set the main loop from another thread will now fail. On the other hand, there is a fairly high probability that such programs would have been failing before due to the lack of any kind of inter-thread memory barriers guarding accesses to this part of Tcl's state.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk | potential incompatibility
Files: files | file ages | folders
SHA1: 9dc4acda9c6c7917faad09c55877318a5fe53e5e
User & Date: dkf 2010-02-27 12:07:04
Context
2010-02-27
19:02
Only look for the needle when it fits in the haystack. [Bug 2960021] check-in: d1aa4b3296 user: dkf tags: trunk
12:07
* generic/tclMain.c (Tcl_Main): [Bug 801429]: Factor out the holding of the client-installed main lo... check-in: 9dc4acda9c user: dkf tags: trunk, potential incompatibility
11:51
Corrections to make things work in the non-cross-compiling case check-in: 5c38ca40c2 user: dkf tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ChangeLog.













1
2
3
4
5
6
7











2010-02-26  Donal K. Fellows  <[email protected]>

	* generic/tclCompCmds.c:   Split this file into two pieces to make it
	* generic/tclCompCmdsSZ.c: easier to work with. It's still two very
				   long files even after the split.

2010-02-26  Reinhard Max  <[email protected]>
>
>
>
>
>
>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2010-02-27  Donal K. Fellows  <[email protected]>

	* generic/tclMain.c (Tcl_Main): [Bug 801429]: Factor out the holding
	of the client-installed main loop function into thread-specific data.

	***POTENTIAL INCOMPATIBILITY***
	Code that previously tried to set the main loop from another thread
	will now fail. On the other hand, there is a fairly high probability
	that such programs would have been failing before due to the lack of
	any kind of inter-thread memory barriers guarding accesses to this
	part of Tcl's state.

2010-02-26  Donal K. Fellows  <[email protected]>

	* generic/tclCompCmds.c:   Split this file into two pieces to make it
	* generic/tclCompCmdsSZ.c: easier to work with. It's still two very
				   long files even after the split.

2010-02-26  Reinhard Max  <[email protected]>

Changes to generic/tclMain.c.

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44





45
46
47
48
49
50
51
..
68
69
70
71
72
73
74

75
76
77
78
79
80
81
..
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
...
247
248
249
250
251
252
253

254
255
256
257
258
259
260
...
395
396
397
398
399
400
401

402
403
404
405
406
407
408
...
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
...
539
540
541
542
543
544
545
546
547
548
549
550
551
552

553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
...
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625


626



























627
628
629
630
631
632
633
 * Copyright (c) 1988-1994 The Regents of the University of California.
 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
 * Copyright (c) 2000 Ajuba Solutions.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tclMain.c,v 1.48 2010/02/24 10:32:17 dkf Exp $
 */

#include "tclInt.h"

#undef TCL_STORAGE_CLASS
#define TCL_STORAGE_CLASS DLLEXPORT

................................................................................
 * Declarations for various library functions and variables (don't want to
 * include tclPort.h here, because people might copy this file out of the Tcl
 * source directory to make their own modified versions).
 */

extern CRTIMPORT int	isatty(int fd);

typedef struct StartupScript {
    Tcl_Obj *path;	/* The filename of the script for *_Main() routines
			 * to [source] as a startup script, or NULL for
			 * none set, meaning enter interactive mode. */
    Tcl_Obj *encoding;	/* The encoding of the startup script file. */
} StartupScript;

static Tcl_ThreadDataKey startupScriptKey;

static Tcl_MainLoopProc *mainLoopProc = NULL;






/*
 * Structure definition for information used to keep the state of an
 * interactive command processor that reads lines from standard input and
 * writes prompts and results to standard output.
 */

................................................................................
				 * commands. */
} InteractiveState;

/*
 * Forward declarations for functions defined later in this file.
 */


static void		Prompt(Tcl_Interp *interp, PromptType *promptPtr);
static void		StdinProc(ClientData clientData, int mask);
 
/*
 *----------------------------------------------------------------------
 *
 * Tcl_SetStartupScript --
................................................................................
 */

void
Tcl_SetStartupScript(
    Tcl_Obj *path,		/* Filesystem path of startup script file */
    const char *encoding)	/* Encoding of the data in that file */
{
    StartupScript *scriptPtr = Tcl_GetThreadData(&startupScriptKey,
	    (int) sizeof(StartupScript));
    Tcl_Obj *newEncoding = NULL;

    if (encoding != NULL) {
	newEncoding = Tcl_NewStringObj(encoding, -1);
    }

    if (scriptPtr->path != NULL) {
	Tcl_DecrRefCount(scriptPtr->path);
    }
    scriptPtr->path = path;
    if (scriptPtr->path != NULL) {
	Tcl_IncrRefCount(scriptPtr->path);
    }

    if (scriptPtr->encoding != NULL) {
	Tcl_DecrRefCount(scriptPtr->encoding);
    }
    scriptPtr->encoding = newEncoding;
    if (scriptPtr->encoding != NULL) {
	Tcl_IncrRefCount(scriptPtr->encoding);
    }
}
 
/*
 *----------------------------------------------------------------------
 *
 * Tcl_GetStartupScript --
................................................................................
 */

Tcl_Obj *
Tcl_GetStartupScript(
    const char **encodingPtr)	/* When not NULL, points to storage for the
				 * (const char *) that points to the
				 * registered encoding name for the startup
				 * script */
{
    StartupScript *scriptPtr = Tcl_GetThreadData(&startupScriptKey,
	    (int) sizeof(StartupScript));

    if (encodingPtr != NULL) {
	if (scriptPtr->encoding == NULL) {
	    *encodingPtr = NULL;
	} else {
	    *encodingPtr = Tcl_GetString(scriptPtr->encoding);
	}
    }
    return scriptPtr->path;
}
 
/*----------------------------------------------------------------------
 *
 * Tcl_SourceRCFile --
 *
 *	This function is typically invoked by Tcl_Main of Tk_Main function to
................................................................................
				 * function to call after most initialization
				 * but before starting to execute commands. */
{
    Tcl_Obj *path, *resultPtr, *argvPtr, *commandPtr = NULL;
    const char *encodingName = NULL;
    PromptType prompt = PROMPT_START;
    int code, length, tty, exitCode = 0;

    Tcl_Channel inChannel, outChannel, errChannel;
    Tcl_Interp *interp;
    Tcl_DString appName;

    Tcl_FindExecutable(argv[0]);

    interp = Tcl_CreateInterp();
................................................................................
     * Get a new value for tty if anyone writes to ::tcl_interactive
     */

    Tcl_LinkVar(interp, "tcl_interactive", (char *) &tty, TCL_LINK_BOOLEAN);
    inChannel = Tcl_GetStdChannel(TCL_STDIN);
    outChannel = Tcl_GetStdChannel(TCL_STDOUT);
    while ((inChannel != NULL) && !Tcl_InterpDeleted(interp)) {

	if (mainLoopProc == NULL) {
	    if (tty) {
		Prompt(interp, &prompt);
		if (Tcl_InterpDeleted(interp)) {
		    break;
		}
		if (Tcl_LimitExceeded(interp)) {
................................................................................
		isPtr->input = inChannel;
		isPtr->tty = tty;
		isPtr->commandPtr = commandPtr;
		isPtr->prompt = prompt;
		isPtr->interp = interp;

		Tcl_UnlinkVar(interp, "tcl_interactive");
		Tcl_LinkVar(interp, "tcl_interactive", (char *) &(isPtr->tty),
			TCL_LINK_BOOLEAN);

		Tcl_CreateChannelHandler(inChannel, TCL_READABLE, StdinProc,
			isPtr);
	    }

	    mainLoopProc();
	    mainLoopProc = NULL;

	    if (inChannel) {
		tty = isPtr->tty;
		Tcl_UnlinkVar(interp, "tcl_interactive");
		Tcl_LinkVar(interp, "tcl_interactive", (char *) &tty,
			TCL_LINK_BOOLEAN);
		prompt = isPtr->prompt;
................................................................................

	/*
	 * This code here only for the (unsupported and deprecated) [checkmem]
	 * command.
	 */

	if (tclMemDumpFileName != NULL) {
	    mainLoopProc = NULL;
	    Tcl_DeleteInterp(interp);
	}
#endif
    }

  done:

    if ((exitCode == 0) && (mainLoopProc != NULL)
	    && !Tcl_LimitExceeded(interp)) {
	/*
	 * If everything has gone OK so far, call the main loop proc, if it
	 * exists. Packages (like Tk) can set it to start processing events at
	 * this point.
	 */

	mainLoopProc();
	mainLoopProc = NULL;
    }
    if (commandPtr != NULL) {
	Tcl_DecrRefCount(commandPtr);
    }

    /*
     * Rather than calling exit, invoke the "exit" command so that users can
................................................................................
 *---------------------------------------------------------------
 *
 * Tcl_SetMainLoop --
 *
 *	Sets an alternative main loop function.
 *
 * Results:
 *	Returns the previously defined main loop function.
 *
 * Side effects:
 *	This function will be called before Tcl exits, allowing for the
 *	creation of an event loop.
 *
 *---------------------------------------------------------------
 */

void
Tcl_SetMainLoop(
    Tcl_MainLoopProc *proc)
{


    mainLoopProc = proc;



























}
 
/*
 *----------------------------------------------------------------------
 *
 * StdinProc --
 *






|







 







|
|
|
|
|
|
|
|
|
|
>
>
>
>
>







 







>







 







|
<






|
|

|
|
|


|
|

|
|
|







 







|

|
<


|


|


|







 







>







 







>







 







|







|







 







|






>









|







 







|












>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
..
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
..
98
99
100
101
102
103
104
105

106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
...
147
148
149
150
151
152
153
154
155
156

157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
...
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
...
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
...
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
...
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
...
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
 * Copyright (c) 1988-1994 The Regents of the University of California.
 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
 * Copyright (c) 2000 Ajuba Solutions.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tclMain.c,v 1.49 2010/02/27 12:07:04 dkf Exp $
 */

#include "tclInt.h"

#undef TCL_STORAGE_CLASS
#define TCL_STORAGE_CLASS DLLEXPORT

................................................................................
 * Declarations for various library functions and variables (don't want to
 * include tclPort.h here, because people might copy this file out of the Tcl
 * source directory to make their own modified versions).
 */

extern CRTIMPORT int	isatty(int fd);

/*
 * The thread-local variables for this file's functions.
 */

typedef struct {
    Tcl_Obj *path;		/* The filename of the script for *_Main()
				 * routines to [source] as a startup script,
				 * or NULL for none set, meaning enter
				 * interactive mode. */
    Tcl_Obj *encoding;		/* The encoding of the startup script file. */
    Tcl_MainLoopProc *mainLoopProc;
				/* Any installed main loop handler. The main
				 * extension that installs these is Tk. */
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;

/*
 * Structure definition for information used to keep the state of an
 * interactive command processor that reads lines from standard input and
 * writes prompts and results to standard output.
 */

................................................................................
				 * commands. */
} InteractiveState;

/*
 * Forward declarations for functions defined later in this file.
 */

static Tcl_MainLoopProc * GetMainLoop(void);
static void		Prompt(Tcl_Interp *interp, PromptType *promptPtr);
static void		StdinProc(ClientData clientData, int mask);
 
/*
 *----------------------------------------------------------------------
 *
 * Tcl_SetStartupScript --
................................................................................
 */

void
Tcl_SetStartupScript(
    Tcl_Obj *path,		/* Filesystem path of startup script file */
    const char *encoding)	/* Encoding of the data in that file */
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    Tcl_Obj *newEncoding = NULL;

    if (encoding != NULL) {
	newEncoding = Tcl_NewStringObj(encoding, -1);
    }

    if (tsdPtr->path != NULL) {
	Tcl_DecrRefCount(tsdPtr->path);
    }
    tsdPtr->path = path;
    if (tsdPtr->path != NULL) {
	Tcl_IncrRefCount(tsdPtr->path);
    }

    if (tsdPtr->encoding != NULL) {
	Tcl_DecrRefCount(tsdPtr->encoding);
    }
    tsdPtr->encoding = newEncoding;
    if (tsdPtr->encoding != NULL) {
	Tcl_IncrRefCount(tsdPtr->encoding);
    }
}
 
/*
 *----------------------------------------------------------------------
 *
 * Tcl_GetStartupScript --
................................................................................
 */

Tcl_Obj *
Tcl_GetStartupScript(
    const char **encodingPtr)	/* When not NULL, points to storage for the
				 * (const char *) that points to the
				 * registered encoding name for the startup
				 * script. */
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);


    if (encodingPtr != NULL) {
	if (tsdPtr->encoding == NULL) {
	    *encodingPtr = NULL;
	} else {
	    *encodingPtr = Tcl_GetString(tsdPtr->encoding);
	}
    }
    return tsdPtr->path;
}
 
/*----------------------------------------------------------------------
 *
 * Tcl_SourceRCFile --
 *
 *	This function is typically invoked by Tcl_Main of Tk_Main function to
................................................................................
				 * function to call after most initialization
				 * but before starting to execute commands. */
{
    Tcl_Obj *path, *resultPtr, *argvPtr, *commandPtr = NULL;
    const char *encodingName = NULL;
    PromptType prompt = PROMPT_START;
    int code, length, tty, exitCode = 0;
    Tcl_MainLoopProc *mainLoopProc;
    Tcl_Channel inChannel, outChannel, errChannel;
    Tcl_Interp *interp;
    Tcl_DString appName;

    Tcl_FindExecutable(argv[0]);

    interp = Tcl_CreateInterp();
................................................................................
     * Get a new value for tty if anyone writes to ::tcl_interactive
     */

    Tcl_LinkVar(interp, "tcl_interactive", (char *) &tty, TCL_LINK_BOOLEAN);
    inChannel = Tcl_GetStdChannel(TCL_STDIN);
    outChannel = Tcl_GetStdChannel(TCL_STDOUT);
    while ((inChannel != NULL) && !Tcl_InterpDeleted(interp)) {
	mainLoopProc = GetMainLoop();
	if (mainLoopProc == NULL) {
	    if (tty) {
		Prompt(interp, &prompt);
		if (Tcl_InterpDeleted(interp)) {
		    break;
		}
		if (Tcl_LimitExceeded(interp)) {
................................................................................
		isPtr->input = inChannel;
		isPtr->tty = tty;
		isPtr->commandPtr = commandPtr;
		isPtr->prompt = prompt;
		isPtr->interp = interp;

		Tcl_UnlinkVar(interp, "tcl_interactive");
		Tcl_LinkVar(interp, "tcl_interactive", (char *) &isPtr->tty,
			TCL_LINK_BOOLEAN);

		Tcl_CreateChannelHandler(inChannel, TCL_READABLE, StdinProc,
			isPtr);
	    }

	    mainLoopProc();
	    Tcl_SetMainLoop(NULL);

	    if (inChannel) {
		tty = isPtr->tty;
		Tcl_UnlinkVar(interp, "tcl_interactive");
		Tcl_LinkVar(interp, "tcl_interactive", (char *) &tty,
			TCL_LINK_BOOLEAN);
		prompt = isPtr->prompt;
................................................................................

	/*
	 * This code here only for the (unsupported and deprecated) [checkmem]
	 * command.
	 */

	if (tclMemDumpFileName != NULL) {
	    Tcl_SetMainLoop(NULL);
	    Tcl_DeleteInterp(interp);
	}
#endif
    }

  done:
    mainLoopProc = GetMainLoop();
    if ((exitCode == 0) && (mainLoopProc != NULL)
	    && !Tcl_LimitExceeded(interp)) {
	/*
	 * If everything has gone OK so far, call the main loop proc, if it
	 * exists. Packages (like Tk) can set it to start processing events at
	 * this point.
	 */

	mainLoopProc();
	Tcl_SetMainLoop(NULL);
    }
    if (commandPtr != NULL) {
	Tcl_DecrRefCount(commandPtr);
    }

    /*
     * Rather than calling exit, invoke the "exit" command so that users can
................................................................................
 *---------------------------------------------------------------
 *
 * Tcl_SetMainLoop --
 *
 *	Sets an alternative main loop function.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	This function will be called before Tcl exits, allowing for the
 *	creation of an event loop.
 *
 *---------------------------------------------------------------
 */

void
Tcl_SetMainLoop(
    Tcl_MainLoopProc *proc)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    tsdPtr->mainLoopProc = proc;
}
 
/*
 *---------------------------------------------------------------
 *
 * GetMainLoop --
 *
 *	Returns the current alternative main loop function.
 *
 * Results:
 *	Returns the previously defined main loop function, or NULL to indicate
 *	that no such function has been installed and standard tclsh behaviour
 *	(i.e., exit once the script is evaluated if not interactive) is
 *	requested..
 *
 * Side effects:
 *	None (other than possible creation of this file's TSD block).
 *
 *---------------------------------------------------------------
 */

static Tcl_MainLoopProc *
GetMainLoop(void)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    return tsdPtr->mainLoopProc;
}
 
/*
 *----------------------------------------------------------------------
 *
 * StdinProc --
 *