summaryrefslogtreecommitdiff
path: root/ports_smp/linux/gnu/src/tx_thread_smp_protect.c
blob: 8a9aa297c123ef425ce47d6998d7416526cc2f6c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/***************************************************************************
 * Copyright (c) 2024 Microsoft Corporation
 * Copyright (c) 2026-present Eclipse ThreadX contributors
 *
 * This program and the accompanying materials are made available under the
 * terms of the MIT License which is available at
 * https://opensource.org/licenses/MIT.
 *
 * SPDX-License-Identifier: MIT
 **************************************************************************/


/**************************************************************************/
/**************************************************************************/
/**                                                                       */
/** ThreadX Component                                                     */
/**                                                                       */
/**   Thread                                                              */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/


#define TX_SOURCE_CODE
#define TX_THREAD_SMP_SOURCE_CODE


/* Include necessary system files.  */

#include "tx_api.h"
#include "tx_thread.h"
#include "tx_timer.h"


/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _tx_thread_smp_protect                            SMP/Linux/GCC     */
/*                                                           6.1          */
/*  AUTHOR                                                                */
/*                                                                        */
/*    William E. Lamie, Microsoft Corporation                             */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function gets protection for running inside the ThreadX        */
/*    source. This is acomplished by a combination of a test-and-set      */
/*    flag and periodically disabling interrupts.                         */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    None                                                                */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    Previous Status Register                                            */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    pthread_self                          Get Linux thread ID           */
/*    GetThreadPriority                     Get current thread priority   */
/*    _tx_thread_smp_core_get               Get the current core ID       */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    ThreadX Source                                                      */
/*                                                                        */
/**************************************************************************/
UINT  _tx_thread_smp_protect(void)
{

pthread_t   current_thread_id;
int         exit_code = 0;
struct sched_param sp;
UINT        core;
UINT        interrupt_posture;
TX_THREAD   *current_thread;
UINT        current_state;

    /* Loop to attempt to get the protection.  */
    do
    {

        /* First, get the critical section.  */
        do
        {


            /* Lock Linux mutex.  */
            _tx_linux_mutex_obtain(&_tx_linux_mutex);

            /* Pickup the current thread ID.  */
            current_thread_id = pthread_self();

            /* Pickup the current core.   */
            core =  _tx_thread_smp_core_get();

            /* Pickup the current thread pointer.  */
            current_thread = _tx_thread_current_ptr[core];

            /* Determine if this is a thread (THREAD_PRIORITY_LOWEST) and it does not
               match the current thread pointer.  */
            if ((_tx_linux_threadx_thread) &&
                ((!current_thread) || (current_thread -> tx_thread_linux_thread_id != current_thread_id)))
            {

                /* This indicates the Linux thread was actually terminated by ThreadX is only
                   being allowed to run in order to cleanup its resources.  */
                _tx_linux_mutex_release_all(&_tx_linux_mutex);

                /* Exit this thread.  */
                pthread_exit((void *)&exit_code);
            }

            /* Determine if this is not actually a thread.  */
            if (!_tx_linux_threadx_thread)
                break;

            /* Now check for terminated or completed state... and preempt disable is not set!   */
            if ((current_thread) && (_tx_thread_preempt_disable == 0))
            {

                /* Pickup current state.  */
                current_state =  current_thread -> tx_thread_state;

                /* Now check for terminated or completed state.  */
                if ((current_state == TX_TERMINATED) || (current_state == TX_COMPLETED))
                {

                    /* Clear the preemption flag.  */
                    current_thread -> tx_thread_linux_deferred_preempt =  TX_FALSE;

                    /* Indicate that this thread was suspended asynchronously.  */
                    current_thread -> tx_thread_linux_suspension_type =  1;

                    /* Save the remaining time-slice and disable it.  */
                    if (_tx_timer_time_slice[core])
                    {

                        current_thread -> tx_thread_time_slice =  _tx_timer_time_slice[core];
                        _tx_timer_time_slice[core] =  0;
                    }

                    /* Clear the current thread pointer.  */
                    _tx_thread_current_ptr[core] =  TX_NULL;

                    /* Clear this mapping entry.  */
                    _tx_linux_virtual_cores[core].tx_thread_smp_core_mapping_thread =               TX_NULL;
                    _tx_linux_virtual_cores[core].tx_thread_smp_core_mapping_linux_thread_id =      0;

                    /* Indicate that this thread is now ready for scheduling again by another core.  */
                    current_thread -> tx_thread_smp_core_control =  1;

                    /* Debug entry.  */
                    _tx_linux_debug_entry_insert("SCHEDULE-thread_terminate_preempt_complete", __FILE__, __LINE__);

                    /* Release the scheduler's semaphore to immediately try again.  */
                    tx_linux_sem_post(&_tx_linux_scheduler_semaphore);

                    /* This indicates the Linux thread was actually terminated by ThreadX is only
                       being allowed to run in order to cleanup its resources.  */
                    _tx_linux_mutex_release_all(&_tx_linux_mutex);

                    /* Exit this thread.  */
                    pthread_exit((void *)&exit_code);
                }
            }

            /* Determine if the deferred preempt flag is set.  */
            if ((current_thread) && (current_thread -> tx_thread_linux_deferred_preempt))
            {

                /* Release the scheduler's semaphore to immediately try again.  */
                tx_linux_sem_post(&_tx_linux_scheduler_semaphore);

                /* Release the protection that is nested.  */
                _tx_linux_mutex_release_all(&_tx_linux_mutex);

                /* Sleep just to let other threads run.  */
                _tx_linux_thread_sleep(1000000);
            }
            else
            {

                /* Get out of the protection loop.  */
                break;
            }
        } while (1);

        /* Setup the returned interrupt posture.  */
        interrupt_posture =  _tx_linux_global_int_disabled_flag;

        /* Determine if the protection is already active for this core.  */
        if (_tx_thread_smp_protection.tx_thread_smp_protect_core == core)
        {

            /* Yes, we have the protection already.  */

            /* Increment the protection count.  */
            _tx_thread_smp_protection.tx_thread_smp_protect_count++;

            /* Set the global interrupt disable value.  */
            _tx_linux_global_int_disabled_flag =  TX_TRUE;

            /* Debug entry.  */
            _tx_linux_debug_entry_insert("PROTECT-obtained-nested", __FILE__, __LINE__);

            /* Get out of the retry loop.  */
            break;
        }
        /* Determine if the protection is available.  */
        else if (_tx_thread_smp_protection.tx_thread_smp_protect_core == 0xFFFFFFFF)
        {

            /* At this point we have the protection.  Setup the protection structure.   */
            _tx_thread_smp_protection.tx_thread_smp_protect_in_force =         TX_TRUE;
            _tx_thread_smp_protection.tx_thread_smp_protect_thread =           current_thread;
            _tx_thread_smp_protection.tx_thread_smp_protect_core =             core;
            _tx_thread_smp_protection.tx_thread_smp_protect_count =            1;
            _tx_thread_smp_protection.tx_thread_smp_protect_linux_thread_id =  current_thread_id;

            /* Set the global interrupt disable value.  */
            _tx_linux_global_int_disabled_flag =  TX_TRUE;

            /* Debug entry.  */
            _tx_linux_debug_entry_insert("PROTECT-obtained", __FILE__, __LINE__);

            /* Get out of the retry loop.  */
            break;
        }
        else
        {

            /* Protection is owned by another core.  */

            /* Release the protection and start over.  */
            _tx_linux_mutex_release(&_tx_linux_mutex);
        }
    } while (1);

    /* Set the global interrupt disable value.  */
    _tx_linux_global_int_disabled_flag =  TX_TRUE;

    /* Return the interrupt posture.  */
    return(interrupt_posture);
}