/**************************************************************************/ /* */ /* Copyright (c) Microsoft Corporation. All rights reserved. */ /* */ /* This software is licensed under the Microsoft Software License */ /* Terms for Microsoft Azure RTOS. Full text of the license can be */ /* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ /* and in the root directory of this software. */ /* */ /**************************************************************************/ /**************************************************************************/ /**************************************************************************/ /** */ /** 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" #include #include extern sem_t _tx_linux_isr_semaphore; extern UINT _tx_linux_timer_waiting; extern pthread_t _tx_linux_timer_id; /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _tx_thread_schedule SMP/Linux/GCC */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function waits for a thread control block pointer to appear in */ /* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ /* in the variable, the corresponding thread is resumed. */ /* */ /* INPUT */ /* */ /* None */ /* */ /* OUTPUT */ /* */ /* None */ /* */ /* CALLS */ /* */ /* _tx_linux_mutex_obtain */ /* _tx_linux_debug_entry_insert */ /* _tx_linux_thread_resume */ /* tx_linux_sem_post */ /* sem_trywait */ /* tx_linux_sem_wait */ /* */ /* CALLED BY */ /* */ /* _tx_initialize_kernel_enter ThreadX entry function */ /* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ /* */ /**************************************************************************/ VOID _tx_thread_schedule(VOID) { UINT core; TX_THREAD *current_thread; TX_THREAD *execute_thread; struct timespec ts; UCHAR preemt_retry = TX_FALSE; /* Loop forever. */ while(1) { /* Lock Linux mutex. */ _tx_linux_mutex_obtain(&_tx_linux_mutex); /* Check for a system error condition. */ if (_tx_linux_global_int_disabled_flag != TX_FALSE) { /* This should not happen... increment the system error counter. */ _tx_linux_system_error++; } /* Debug entry. */ _tx_linux_debug_entry_insert("SCHEDULE-wake_up", __FILE__, __LINE__); /* Loop through each virtual core to look for an idle core. */ for (core = 0; core < TX_THREAD_SMP_MAX_CORES; core++) { /* Pickup the current thread pointer for this core. */ current_thread = _tx_thread_current_ptr[core]; /* Determine if the thread's deferred preemption flag is set. */ if ((current_thread) && (current_thread -> tx_thread_linux_deferred_preempt)) { if (_tx_thread_preempt_disable) { /* Preemption disabled. Retry. */ preemt_retry = TX_TRUE; break; } if (current_thread -> tx_thread_state != TX_TERMINATED) { /* Suspend the thread to simulate preemption. Note that the thread is suspended BEFORE the protection get flag is checked to ensure there is not a race condition between this thread and the update of that flag. */ _tx_linux_thread_suspend(current_thread -> tx_thread_linux_thread_id); /* 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-core_preempt_complete", __FILE__, __LINE__); } /* Determine if this core is idle. */ if (_tx_thread_current_ptr[core] == TX_NULL) { /* Yes, this core is idle, determine if there is a thread that can be scheduled for it. */ /* Pickup the execute thread pointer. */ execute_thread = _tx_thread_execute_ptr[core]; /* Is there a thread that is ready to execute on this core? */ if ((execute_thread) && (execute_thread -> tx_thread_smp_core_control)) { /* Yes! We have a thread to execute. Note that the critical section is already active from the scheduling loop above. */ /* Setup the current thread pointer. */ _tx_thread_current_ptr[core] = execute_thread; /* Remember the virtual core in the thread control block. */ execute_thread -> tx_thread_linux_virtual_core = core; /* Setup the virtual core mapping structure. */ _tx_linux_virtual_cores[core].tx_thread_smp_core_mapping_thread = execute_thread; _tx_linux_virtual_cores[core].tx_thread_smp_core_mapping_linux_thread_id = execute_thread -> tx_thread_linux_thread_id; /* Clear the execution control flag. */ execute_thread -> tx_thread_smp_core_control = 0; /* Increment the run count for this thread. */ execute_thread -> tx_thread_run_count++; /* Setup time-slice, if present. */ _tx_timer_time_slice[core] = execute_thread -> tx_thread_time_slice; /* Determine how the thread was last suspended. */ if (execute_thread -> tx_thread_linux_suspension_type == 1) { /* Clear the suspension type. */ execute_thread -> tx_thread_linux_suspension_type = 0; /* Debug entry. */ _tx_linux_debug_entry_insert("SCHEDULE-resume_thread", __FILE__, __LINE__); /* Pseudo interrupt suspension. The thread is not waiting on its run semaphore. */ _tx_linux_thread_resume(execute_thread -> tx_thread_linux_thread_id); } else if (execute_thread -> tx_thread_linux_suspension_type == 2) { /* Clear the suspension type. */ execute_thread -> tx_thread_linux_suspension_type = 0; /* Debug entry. */ _tx_linux_debug_entry_insert("SCHEDULE-release_sem", __FILE__, __LINE__); /* Make sure semaphore is 0. */ while(!sem_trywait(&execute_thread -> tx_thread_linux_thread_run_semaphore)); /* Let the thread run again by releasing its run semaphore. */ tx_linux_sem_post(&execute_thread -> tx_thread_linux_thread_run_semaphore); /* Block timer ISR. */ if(_tx_linux_timer_waiting) { /* It is woken up by timer ISR. */ /* Let ThreadX thread wake up first. */ tx_linux_sem_wait(&_tx_linux_scheduler_semaphore); /* Wake up timer ISR. */ tx_linux_sem_post(&_tx_linux_isr_semaphore); } else { /* It is woken up by TX_THREAD. */ /* Suspend timer thread and let ThreadX thread wake up first. */ _tx_linux_thread_suspend(_tx_linux_timer_id); tx_linux_sem_wait(&_tx_linux_scheduler_semaphore); _tx_linux_thread_resume(_tx_linux_timer_id); } } else { /* System error, increment the counter. */ _tx_linux_system_error++; } } } } if (preemt_retry) { /* Unlock linux mutex. */ _tx_linux_mutex_release_all(&_tx_linux_mutex); /* Let user thread run to reset _tx_thread_preempt_disable. */ _tx_linux_thread_sleep(1); preemt_retry = TX_FALSE; continue; } /* Debug entry. */ _tx_linux_debug_entry_insert("SCHEDULE-self_suspend_sem", __FILE__, __LINE__); /* Unlock linux mutex. */ _tx_linux_mutex_release_all(&_tx_linux_mutex); /* Now suspend the main thread so the application thread can run. */ clock_gettime(CLOCK_REALTIME, &ts); ts.tv_nsec += 2000000; if (ts.tv_nsec >= 1000000000) { ts.tv_nsec -= 1000000000; ts.tv_sec++; } tx_linux_sem_timedwait(&_tx_linux_scheduler_semaphore, &ts); clock_gettime(CLOCK_REALTIME, &ts); } } /* Define the ThreadX Linux mutex get, release, and release all functions. */ void _tx_linux_mutex_obtain(TX_LINUX_MUTEX *mutex) { TX_THREAD *thread_ptr; pthread_t current_thread_id; UINT i; /* Pickup the current thread ID. */ current_thread_id = pthread_self(); /* Is the protection owned? */ if (mutex -> tx_linux_mutex_owner == current_thread_id) { /* Simply increment the nested counter. */ mutex -> tx_linux_mutex_nested_count++; } else { /* Loop to find a thread matching this ID. */ i = 0; do { /* Pickup the thread pointer. */ thread_ptr = _tx_thread_current_ptr[i]; /* Is this thread obtaining the mutex? */ if ((thread_ptr) && (thread_ptr -> tx_thread_linux_thread_id == current_thread_id)) { /* We have found the thread, get out of the loop. */ break; } /* Look at next core. */ i++; } while (i < TX_THREAD_SMP_MAX_CORES); /* Determine if we found a thread. */ if (i >= TX_THREAD_SMP_MAX_CORES) { /* Set the thread pointer to NULL to indicate a thread was not found. */ thread_ptr = TX_NULL; } /* If a thread was found, indicate the thread is attempting to access the mutex. */ if (thread_ptr) { /* Yes, current ThreadX thread attempting to get the mutex - set the flag. */ thread_ptr -> tx_thread_linux_mutex_access = TX_TRUE; } /* Get the Linux mutex. */ pthread_mutex_lock(&mutex -> tx_linux_mutex); /* At this point we have the mutex. */ /* Clear the mutex access flag for the thread. */ if (thread_ptr) { /* Yes, clear the current ThreadX thread attempting to get the mutex. */ thread_ptr -> tx_thread_linux_mutex_access = TX_FALSE; } /* Increment the nesting counter. */ mutex -> tx_linux_mutex_nested_count = 1; /* Remember the owner. */ mutex -> tx_linux_mutex_owner = pthread_self(); } } void _tx_linux_mutex_release(TX_LINUX_MUTEX *mutex) { pthread_t current_thread_id; /* Pickup the current thread ID. */ current_thread_id = pthread_self(); /* Ensure the caller is the mutex owner. */ if (mutex -> tx_linux_mutex_owner == current_thread_id) { /* Determine if there is protection. */ if (mutex -> tx_linux_mutex_nested_count) { /* Decrement the nesting counter. */ mutex -> tx_linux_mutex_nested_count--; /* Determine if the critical section is now being released. */ if (mutex -> tx_linux_mutex_nested_count == 0) { /* Yes, it is being released clear the owner. */ mutex -> tx_linux_mutex_owner = 0; /* Finally, release the mutex. */ if (pthread_mutex_unlock(&mutex -> tx_linux_mutex) != 0) { /* Increment the system error counter. */ _tx_linux_system_error++; } /* Just in case, make sure there the mutex is not owned. */ while (pthread_mutex_unlock(&mutex -> tx_linux_mutex) == 0) { /* Increment the system error counter. */ _tx_linux_system_error++; } /* Relinquish to other ready threads. */ _tx_linux_thread_sleep(1000); } } } else { /* Increment the system error counter. */ _tx_linux_system_error++; } } void _tx_linux_mutex_release_all(TX_LINUX_MUTEX *mutex) { /* Ensure the caller is the mutex owner. */ if (mutex -> tx_linux_mutex_owner == pthread_self()) { /* Determine if there is protection. */ if (mutex -> tx_linux_mutex_nested_count) { /* Clear the nesting counter. */ mutex -> tx_linux_mutex_nested_count = 0; /* Yes, it is being release clear the owner. */ mutex -> tx_linux_mutex_owner = 0; /* Finally, release the mutex. */ if (pthread_mutex_unlock(&mutex -> tx_linux_mutex) != 0) { /* Increment the system error counter. */ _tx_linux_system_error++; } /* Just in case, make sure there the mutex is not owned. */ while (pthread_mutex_unlock(&mutex -> tx_linux_mutex) == 0) { /* Increment the system error counter. */ _tx_linux_system_error++; } } } else { /* Increment the system error counter. */ _tx_linux_system_error++; } } void _tx_thread_delete_port_completion(TX_THREAD *thread_ptr, UINT tx_interrupt_save) { INT linux_status; sem_t *threadrunsemaphore; pthread_t thread_id; thread_id = thread_ptr -> tx_thread_linux_thread_id; threadrunsemaphore = &(thread_ptr -> tx_thread_linux_thread_run_semaphore); _tx_thread_smp_unprotect(tx_interrupt_save); do { linux_status = pthread_cancel(thread_id); if(linux_status != EAGAIN) { break; } _tx_linux_thread_resume(thread_id); tx_linux_sem_post(threadrunsemaphore); _tx_linux_thread_sleep(1000000); } while (1); pthread_join(thread_id, NULL); sem_destroy(threadrunsemaphore); tx_interrupt_save = _tx_thread_smp_protect(); } void _tx_thread_reset_port_completion(TX_THREAD *thread_ptr, UINT tx_interrupt_save) { INT linux_status; sem_t *threadrunsemaphore; pthread_t thread_id; thread_id = thread_ptr -> tx_thread_linux_thread_id; threadrunsemaphore = &(thread_ptr -> tx_thread_linux_thread_run_semaphore); _tx_thread_smp_unprotect(tx_interrupt_save); do { linux_status = pthread_cancel(thread_id); if(linux_status != EAGAIN) { break; } _tx_linux_thread_resume(thread_id); tx_linux_sem_post(threadrunsemaphore); _tx_linux_thread_sleep(1000000); } while (1); pthread_join(thread_id, NULL); sem_destroy(threadrunsemaphore); tx_interrupt_save = _tx_thread_smp_protect(); }