Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions common/src/tx_thread_delete.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/

// Some portions generated by Claude Code (Opus 5).


/**************************************************************************/
/**************************************************************************/
Expand Down Expand Up @@ -95,6 +97,31 @@ UINT status;
}
}

/* The state is terminal, but a terminal state on its own is not authorization to
release the control block. Both paths that produce one -- thread completion in
_tx_thread_shell_entry and thread termination in _tx_thread_terminate -- publish
TX_COMPLETED or TX_TERMINATED, and then run this thread's exit notification
callback, before the thread has been detached from the ready list and before
those services have finished with the pointer they hold to it. The suspending
flag is set for exactly that interval, so a thread whose flag is still set is
part-way through the transition. */
if (status == TX_SUCCESS)
{

/* Is the completion or termination transition still in progress? */
if (thread_ptr -> tx_thread_suspending == TX_TRUE)
{

/* Restore interrupts. */
TX_RESTORE

/* Yes, refuse the delete rather than unlink a thread the scheduler is
still holding. The condition is transient: the caller may retry once
the transition has finished. */
status = TX_DELETE_ERROR;
}
}

/* Determine if the delete operation is okay. */
if (status == TX_SUCCESS)
{
Expand Down
24 changes: 24 additions & 0 deletions common/src/tx_thread_reset.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/

// Some portions generated by Claude Code (Opus 5).


/**************************************************************************/
/**************************************************************************/
Expand Down Expand Up @@ -104,6 +106,28 @@ UINT status;
status = TX_NOT_DONE;
}
}

/* The state is terminal, but a terminal state on its own is not authorization
to rebuild the stack and move the thread back to TX_SUSPENDED. Both paths
that produce one -- thread completion in _tx_thread_shell_entry and thread
termination in _tx_thread_terminate -- publish TX_COMPLETED or
TX_TERMINATED, and then run this thread's exit notification callback, before
the thread has been detached from the ready list. The suspending flag is
set for exactly that interval. Resetting inside it would also defeat the
protection _tx_thread_system_resume relies on, which refuses to cancel a
suspension only while the state is still terminal. */
if (status == TX_SUCCESS)
{

/* Is the completion or termination transition still in progress? */
if (thread_ptr -> tx_thread_suspending == TX_TRUE)
{

/* Yes, refuse the reset. The condition is transient: the caller may
retry once the transition has finished. */
status = TX_NOT_DONE;
}
}
}

/* Is the request valid? */
Expand Down
22 changes: 22 additions & 0 deletions common/src/tx_thread_shell_entry.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/

// Some portions generated by Claude Code (Opus 5).


/**************************************************************************/
/**************************************************************************/
Expand Down Expand Up @@ -134,6 +136,17 @@ VOID (*entry_exit_notify)(TX_THREAD *notify_thread_ptr, UINT type);

#ifdef TX_NOT_INTERRUPTABLE

/* Set the suspending flag, so that the completion transition is marked as in
progress for the same interval it is marked in the interruptable
configuration below. Nothing in this configuration needs the flag to cancel
an interrupted suspension -- interrupts stay disabled through the whole
transition -- but the notification callback and the completion extension
below are application code, reached by a direct call, and interrupt lockout
does not stop either of them from calling a thread lifecycle service on this
same control block. The flag is what _tx_thread_delete and _tx_thread_reset
test in order to refuse one. */
thread_ptr -> tx_thread_suspending = TX_TRUE;

#ifndef TX_DISABLE_NOTIFY_CALLBACKS

/* Determine if an application callback routine is specified. */
Expand All @@ -148,6 +161,15 @@ VOID (*entry_exit_notify)(TX_THREAD *notify_thread_ptr, UINT type);
/* Perform any additional activities for tool or user purpose. */
TX_THREAD_COMPLETED_EXTENSION(thread_ptr)

/* Clear the suspending flag. It has to be cleared here rather than after the
call below, because _tx_thread_system_ni_suspend returns to the scheduler for
a thread that is the current thread, which this one is, and so does not come
back. Clearing it afterwards would leave a normally completed thread marked
as transitioning for ever and therefore permanently undeletable. Clearing it
here loses nothing: everything from this point to the detachment runs with
interrupts disabled and calls no application code. */
thread_ptr -> tx_thread_suspending = TX_FALSE;

/* Call actual non-interruptable thread suspension routine. */
_tx_thread_system_ni_suspend(thread_ptr, ((ULONG) 0));

Expand Down
47 changes: 33 additions & 14 deletions common/src/tx_thread_terminate.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/

// Some portions generated by Claude Code (Opus 5).


/**************************************************************************/
/**************************************************************************/
Expand Down Expand Up @@ -133,6 +135,16 @@ ULONG suspension_sequence;

#ifdef TX_NOT_INTERRUPTABLE

/* Set the suspending flag, so that the termination transition is marked
as in progress for the same interval it is marked in the interruptable
configuration below. Nothing in this configuration needs the flag to
cancel an interrupted suspension -- interrupts stay disabled through
the whole transition -- but the notification callback below is
application code, reached by a direct call, and interrupt lockout does
not stop it from calling a thread lifecycle service on this same
control block. The flag is cleared in the common tail below. */
thread_ptr -> tx_thread_suspending = TX_TRUE;

#ifndef TX_DISABLE_NOTIFY_CALLBACKS

/* Determine if an application callback routine is specified. */
Expand Down Expand Up @@ -225,20 +237,13 @@ ULONG suspension_sequence;
(suspend_cleanup)(thread_ptr, suspension_sequence);
}

#ifndef TX_NOT_INTERRUPTABLE

/* Disable interrupts. */
TX_DISABLE
#endif

/* Clear the suspending flag. */
thread_ptr -> tx_thread_suspending = TX_FALSE;

#ifndef TX_NOT_INTERRUPTABLE

/* Restore interrupts. */
TX_RESTORE
#endif
/* The suspending flag deliberately stays set here. It used to be
cleared at this point, which left the terminated extension and the
notification callback below -- both application code -- free to delete
or reset this control block while this service still held a pointer to
it and still had mutex-release processing to do. It is now cleared
once, in the common tail below, after the last dereference of the
target. */

/* Perform any additional activities for tool or user purpose. */
TX_THREAD_TERMINATED_EXTENSION(thread_ptr)
Expand Down Expand Up @@ -282,6 +287,20 @@ ULONG suspension_sequence;
TX_DISABLE
#endif

/* Clear the suspending flag. This is the one place the end of the
termination transition is published, and it is reached from both branches
above, after the notification callback and after the mutex-release
processing, which is the last thing in this service to dereference the
target. Until this store, _tx_thread_delete and _tx_thread_reset refuse
the target. In the interruptable ready branch the flag is already false,
because _tx_thread_system_suspend cleared it when it detached the thread,
so this is a second store of a value the flag already holds; that is
cheaper than testing for it and it keeps the transition to a single clear
site. No suspension-initiating service can have set it again in between:
every one of them acts on a thread that is ready or suspended, and this
thread is terminated. */
thread_ptr -> tx_thread_suspending = TX_FALSE;

/* Enable preemption. */
_tx_thread_preempt_disable--;

Expand Down
27 changes: 27 additions & 0 deletions common_smp/src/tx_thread_delete.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/

// Some portions generated by Claude Code (Opus 5).


/**************************************************************************/
/**************************************************************************/
Expand Down Expand Up @@ -95,6 +97,31 @@ UINT status;
}
}

/* The state is terminal, but a terminal state on its own is not authorization to
release the control block. Both paths that produce one -- thread completion in
_tx_thread_shell_entry and thread termination in _tx_thread_terminate -- publish
TX_COMPLETED or TX_TERMINATED, and then run this thread's exit notification
callback, before the thread has been detached from the ready list and before
those services have finished with the pointer they hold to it. The suspending
flag is set for exactly that interval, so a thread whose flag is still set is
part-way through the transition. */
if (status == TX_SUCCESS)
{

/* Is the completion or termination transition still in progress? */
if (thread_ptr -> tx_thread_suspending == TX_TRUE)
{

/* Restore interrupts. */
TX_RESTORE

/* Yes, refuse the delete rather than unlink a thread the scheduler is
still holding. The condition is transient: the caller may retry once
the transition has finished. */
status = TX_DELETE_ERROR;
}
}

/* Determine if the delete operation is okay. */
if (status == TX_SUCCESS)
{
Expand Down
24 changes: 24 additions & 0 deletions common_smp/src/tx_thread_reset.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/

// Some portions generated by Claude Code (Opus 5).


/**************************************************************************/
/**************************************************************************/
Expand Down Expand Up @@ -104,6 +106,28 @@ UINT status;
status = TX_NOT_DONE;
}
}

/* The state is terminal, but a terminal state on its own is not authorization
to rebuild the stack and move the thread back to TX_SUSPENDED. Both paths
that produce one -- thread completion in _tx_thread_shell_entry and thread
termination in _tx_thread_terminate -- publish TX_COMPLETED or
TX_TERMINATED, and then run this thread's exit notification callback, before
the thread has been detached from the ready list. The suspending flag is
set for exactly that interval. Resetting inside it would also defeat the
protection _tx_thread_system_resume relies on, which refuses to cancel a
suspension only while the state is still terminal. */
if (status == TX_SUCCESS)
{

/* Is the completion or termination transition still in progress? */
if (thread_ptr -> tx_thread_suspending == TX_TRUE)
{

/* Yes, refuse the reset. The condition is transient: the caller may
retry once the transition has finished. */
status = TX_NOT_DONE;
}
}
}

/* Is the request valid? */
Expand Down
22 changes: 22 additions & 0 deletions common_smp/src/tx_thread_shell_entry.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/

// Some portions generated by Claude Code (Opus 5).


/**************************************************************************/
/**************************************************************************/
Expand Down Expand Up @@ -134,6 +136,17 @@ VOID (*entry_exit_notify)(TX_THREAD *notify_thread_ptr, UINT type);

#ifdef TX_NOT_INTERRUPTABLE

/* Set the suspending flag, so that the completion transition is marked as in
progress for the same interval it is marked in the interruptable
configuration below. Nothing in this configuration needs the flag to cancel
an interrupted suspension -- interrupts stay disabled through the whole
transition -- but the notification callback and the completion extension
below are application code, reached by a direct call, and interrupt lockout
does not stop either of them from calling a thread lifecycle service on this
same control block. The flag is what _tx_thread_delete and _tx_thread_reset
test in order to refuse one. */
thread_ptr -> tx_thread_suspending = TX_TRUE;

#ifndef TX_DISABLE_NOTIFY_CALLBACKS

/* Determine if an application callback routine is specified. */
Expand All @@ -148,6 +161,15 @@ VOID (*entry_exit_notify)(TX_THREAD *notify_thread_ptr, UINT type);
/* Perform any additional activities for tool or user purpose. */
TX_THREAD_COMPLETED_EXTENSION(thread_ptr)

/* Clear the suspending flag. It has to be cleared here rather than after the
call below, because _tx_thread_system_ni_suspend returns to the scheduler for
a thread that is the current thread, which this one is, and so does not come
back. Clearing it afterwards would leave a normally completed thread marked
as transitioning for ever and therefore permanently undeletable. Clearing it
here loses nothing: everything from this point to the detachment runs with
interrupts disabled and calls no application code. */
thread_ptr -> tx_thread_suspending = TX_FALSE;

/* Call actual non-interruptable thread suspension routine. */
_tx_thread_system_ni_suspend(thread_ptr, ((ULONG) 0));

Expand Down
47 changes: 33 additions & 14 deletions common_smp/src/tx_thread_terminate.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/

// Some portions generated by Claude Code (Opus 5).


/**************************************************************************/
/**************************************************************************/
Expand Down Expand Up @@ -133,6 +135,16 @@ ULONG suspension_sequence;

#ifdef TX_NOT_INTERRUPTABLE

/* Set the suspending flag, so that the termination transition is marked
as in progress for the same interval it is marked in the interruptable
configuration below. Nothing in this configuration needs the flag to
cancel an interrupted suspension -- interrupts stay disabled through
the whole transition -- but the notification callback below is
application code, reached by a direct call, and interrupt lockout does
not stop it from calling a thread lifecycle service on this same
control block. The flag is cleared in the common tail below. */
thread_ptr -> tx_thread_suspending = TX_TRUE;

#ifndef TX_DISABLE_NOTIFY_CALLBACKS

/* Determine if an application callback routine is specified. */
Expand Down Expand Up @@ -225,20 +237,13 @@ ULONG suspension_sequence;
(suspend_cleanup)(thread_ptr, suspension_sequence);
}

#ifndef TX_NOT_INTERRUPTABLE

/* Disable interrupts. */
TX_DISABLE
#endif

/* Clear the suspending flag. */
thread_ptr -> tx_thread_suspending = TX_FALSE;

#ifndef TX_NOT_INTERRUPTABLE

/* Restore interrupts. */
TX_RESTORE
#endif
/* The suspending flag deliberately stays set here. It used to be
cleared at this point, which left the terminated extension and the
notification callback below -- both application code -- free to delete
or reset this control block while this service still held a pointer to
it and still had mutex-release processing to do. It is now cleared
once, in the common tail below, after the last dereference of the
target. */

/* Perform any additional activities for tool or user purpose. */
TX_THREAD_TERMINATED_EXTENSION(thread_ptr)
Expand Down Expand Up @@ -282,6 +287,20 @@ ULONG suspension_sequence;
TX_DISABLE
#endif

/* Clear the suspending flag. This is the one place the end of the
termination transition is published, and it is reached from both branches
above, after the notification callback and after the mutex-release
processing, which is the last thing in this service to dereference the
target. Until this store, _tx_thread_delete and _tx_thread_reset refuse
the target. In the interruptable ready branch the flag is already false,
because _tx_thread_system_suspend cleared it when it detached the thread,
so this is a second store of a value the flag already holds; that is
cheaper than testing for it and it keeps the transition to a single clear
site. No suspension-initiating service can have set it again in between:
every one of them acts on a thread that is ready or suspended, and this
thread is terminated. */
thread_ptr -> tx_thread_suspending = TX_FALSE;

/* Enable preemption. */
_tx_thread_preempt_disable--;

Expand Down
Loading
Loading