Tcl Source Code

Check-in [ab87ef464e]
Login

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

Overview
Comment:Fix for [39680865953cce4f], Basic nonblocking write-only refchan hangs.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | bug-3968086595
Files: files | file ages | folders
SHA3-256: ab87ef464e12c190fc7a28672aa45d68499e5d1229e657b05b2858c2c31c7d5f
User & Date: pooryorick 2024-08-13 13:45:38.623
References
2024-08-13
13:47 Pending ticket [3968086595]: Basic nonblocking write-only refchan hangs plus 4 other changes artifact: 08f15f7f32 user: pooryorick
Context
2024-08-13
13:45
Fix for [39680865953cce4f], Basic nonblocking write-only refchan hangs. Closed-Leaf check-in: ab87ef464e user: pooryorick tags: bug-3968086595
13:32
Fix [2f22a7364d]: cesu-8 encoding gives same result for different strings check-in: da1af6e174 user: jan.nijtmans tags: trunk, main
Changes
Unified Diff Ignore Whitespace Patch
Changes to generic/tclIO.c.
8756
8757
8758
8759
8760
8761
8762



















8763
8764
8765
8766
8767
8768
8769
		TclChannelPreserve((Tcl_Channel)chanPtr);
		statePtr->timerChanPtr = chanPtr;
		statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME,
			ChannelTimerProc, chanPtr);
	    }
	}
    }



















    ChanWatch(chanPtr, mask);
}

/*
 *----------------------------------------------------------------------
 *
 * ChannelTimerProc --







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







8756
8757
8758
8759
8760
8761
8762
8763
8764
8765
8766
8767
8768
8769
8770
8771
8772
8773
8774
8775
8776
8777
8778
8779
8780
8781
8782
8783
8784
8785
8786
8787
8788
		TclChannelPreserve((Tcl_Channel)chanPtr);
		statePtr->timerChanPtr = chanPtr;
		statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME,
			ChannelTimerProc, chanPtr);
	    }
	}
    }

    if (!statePtr->timer
	    && mask & TCL_WRITABLE
	    && GotFlag(statePtr, CHANNEL_NONBLOCKING)
	    && (
		statePtr->curOutPtr
		&&
		!IsBufferEmpty(statePtr->curOutPtr)
		&&
		!IsBufferFull(statePtr->curOutPtr)
	   )
    ) {
	TclChannelPreserve((Tcl_Channel)chanPtr);
	statePtr->timerChanPtr = chanPtr;
	statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME,
		ChannelTimerProc,chanPtr);
    }


    ChanWatch(chanPtr, mask);
}

/*
 *----------------------------------------------------------------------
 *
 * ChannelTimerProc --
8783
8784
8785
8786
8787
8788
8789

8790
8791
8792
8793
8794
8795
8796
8797
8798
8799
8800
8801
8802
8803
8804
8805
8806
8807


8808













8809

8810
8811

8812
8813
8814
8815
8816
8817
8818
static void
ChannelTimerProc(
    void *clientData)
{
    Channel *chanPtr = (Channel *)clientData;
    /* State info for channel */
    ChannelState *statePtr = chanPtr->state;


    if (chanPtr->typePtr == NULL) {
	statePtr->timer = NULL;
	TclChannelRelease((Tcl_Channel)statePtr->timerChanPtr);
	statePtr->timerChanPtr = NULL;
    } else {
	if (!GotFlag(statePtr, CHANNEL_NEED_MORE_DATA)
		&& (statePtr->interestMask & TCL_READABLE)
		&& (statePtr->inQueueHead != NULL)
		&& IsBufferReady(statePtr->inQueueHead)) {
	    /*
	     * Restart the timer in case a channel handler reenters the event loop
	     * before UpdateInterest gets called by Tcl_NotifyChannel.
	     */
	    statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME,
		ChannelTimerProc,chanPtr);
	    Tcl_Preserve(statePtr);
	    Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_READABLE);


	    Tcl_Release(statePtr);













	} else {

	    statePtr->timer = NULL;
	    UpdateInterest(chanPtr);

	    TclChannelRelease((Tcl_Channel)statePtr->timerChanPtr);
	    statePtr->timerChanPtr = NULL;
	}
    }
}

static void







>
















<

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


>







8802
8803
8804
8805
8806
8807
8808
8809
8810
8811
8812
8813
8814
8815
8816
8817
8818
8819
8820
8821
8822
8823
8824
8825

8826
8827
8828
8829
8830
8831
8832
8833
8834
8835
8836
8837
8838
8839
8840
8841
8842
8843
8844
8845
8846
8847
8848
8849
8850
8851
8852
8853
8854
static void
ChannelTimerProc(
    void *clientData)
{
    Channel *chanPtr = (Channel *)clientData;
    /* State info for channel */
    ChannelState *statePtr = chanPtr->state;
    int notified = 0;

    if (chanPtr->typePtr == NULL) {
	statePtr->timer = NULL;
	TclChannelRelease((Tcl_Channel)statePtr->timerChanPtr);
	statePtr->timerChanPtr = NULL;
    } else {
	if (!GotFlag(statePtr, CHANNEL_NEED_MORE_DATA)
		&& (statePtr->interestMask & TCL_READABLE)
		&& (statePtr->inQueueHead != NULL)
		&& IsBufferReady(statePtr->inQueueHead)) {
	    /*
	     * Restart the timer in case a channel handler reenters the event loop
	     * before UpdateInterest gets called by Tcl_NotifyChannel.
	     */
	    statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME,
		ChannelTimerProc,chanPtr);

	    Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_READABLE);
	    notified = 1;
	} 

	if (chanPtr->typePtr != NULL
	    && statePtr->interestMask & TCL_WRITABLE
	    && GotFlag(statePtr, CHANNEL_NONBLOCKING)
	    && !GotFlag(statePtr, BG_FLUSH_SCHEDULED)) {
	    /*
	     * Restart the timer in case a channel handler reenters the event loop
	     * before UpdateInterest gets called by Tcl_NotifyChannel.
	     */
	    statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME,
		ChannelTimerProc,chanPtr);
	    Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_WRITABLE);
	    notified = 1;
	}

	if (!notified) {
	    statePtr->timer = NULL;
	    UpdateInterest(chanPtr);
	    /* Was set in UpdateInterest. */
	    TclChannelRelease((Tcl_Channel)statePtr->timerChanPtr);
	    statePtr->timerChanPtr = NULL;
	}
    }
}

static void
Changes to tests/io.test.
6364
6365
6366
6367
6368
6369
6370












































































































6371
6372
6373
6374
6375
6376
6377
        close $chan
        set ::io-44.7-result success
    } [namespace current]]
    vwait ::io-44.7-result
    set ::io-44.7-result
} -result success













































































































makeFile "foo bar" foo

test io-45.1 {DeleteFileEvent, cleanup on close} {fileevent} {
    set f [open $path(foo) r]
    fileevent $f readable [namespace code {
	lappend x "binding triggered: \"[gets $f]\""
	fileevent $f readable {}







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







6364
6365
6366
6367
6368
6369
6370
6371
6372
6373
6374
6375
6376
6377
6378
6379
6380
6381
6382
6383
6384
6385
6386
6387
6388
6389
6390
6391
6392
6393
6394
6395
6396
6397
6398
6399
6400
6401
6402
6403
6404
6405
6406
6407
6408
6409
6410
6411
6412
6413
6414
6415
6416
6417
6418
6419
6420
6421
6422
6423
6424
6425
6426
6427
6428
6429
6430
6431
6432
6433
6434
6435
6436
6437
6438
6439
6440
6441
6442
6443
6444
6445
6446
6447
6448
6449
6450
6451
6452
6453
6454
6455
6456
6457
6458
6459
6460
6461
6462
6463
6464
6465
6466
6467
6468
6469
6470
6471
6472
6473
6474
6475
6476
6477
6478
6479
6480
6481
6482
6483
6484
6485
        close $chan
        set ::io-44.7-result success
    } [namespace current]]
    vwait ::io-44.7-result
    set ::io-44.7-result
} -result success


test io-44.8 {write-only refchan should not hang} -setup {
    catch {namespace delete rc}
    namespace eval rc {
	namespace export *
	namespace ensemble create

	proc buffer chan {
	    namespace upvar chan_$chan buffer buffer
	    return $buffer
	}

	proc initialize {chan mode} {
	    namespace eval chan_$chan {
		variable buffer {}
		variable watch {}
		variable writetask {}
	    }
	    return {initialize finalize watch write}
	}


	proc finalize chan {
	    namespace upvar chan_$chan writetask writetask
	    after cancel $writetask
	    set writetask {}
	    namespace delete chan_$chan
	}


	proc watch {chan spec} {
	    set channs [namespace current]::chan_$chan
	    namespace upvar $channs watch watch writetask writetask
	    set watch $spec
	    after cancel $writetask
	    if {{write} in $spec} {
		set writetask [after 0 [list after idle [
		    list ::apply {{channs chan} {
			if {[namespace exists $channs]} {
			    chan postevent $chan write
			}
		    }} $channs $chan]]]
	    } else {
		set writetask {}
	    }
	    return
	}


	proc write {chan data} {
	    set channs [namespace current]::chan_$chan
	    namespace upvar $channs buffer buffer watch watch \
		writetask writetask
	    append buffer $data
	    after cancel writetask
	    if {{write} in $watch} {
		set writetask [after 0 [list after idle [
		    list ::apply {{channs chan} {
			if {[namespace exists $channs]} {
			    chan postevent $chan write
			}
		}} $channs $chan]]]
	    } else {
		set writetask {}
	    }
	    return [string length $data]
	}


	proc post {chan side} {
	    set ns chan_$chan
	    if [namespace exists $ns] {
		chan postevent $chan $side
	    }
	    return
	}
    }
} -cleanup {
    namespace delete rc
} -body {
    variable done
    set chan [chan create write [namespace which rc]]
    try {
	chan configure $chan -blocking 0
	coroutine c1 apply [list chan {
	    variable done
	    after 0 [list [info coroutine]] 
	    set written 0
	    yield
	    chan event $chan writable [list [info coroutine]]
	    # Perform enough to exercise more than one buffer.
	    while {[incr i] < 500000}  {
		yield
		puts $chan $i
		set written [expr {$written + [string length $i] + 1}]
	    }
	    chan configure $chan -blocking 1
	    flush $chan
	    set buffersize [string length [rc buffer $chan]]
	    set done [list $written $buffersize]
	} [namespace current]] $chan
	vwait [namespace current]::done
    } finally {
	close $chan
    }
    return $done
} -result {3388888 3388888}

makeFile "foo bar" foo

test io-45.1 {DeleteFileEvent, cleanup on close} {fileevent} {
    set f [open $path(foo) r]
    fileevent $f readable [namespace code {
	lappend x "binding triggered: \"[gets $f]\""
	fileevent $f readable {}