diff --git a/nx/include/switch.h b/nx/include/switch.h index b3ed59cc..89dfe388 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -28,6 +28,7 @@ extern "C" { #include "switch/kernel/condvar.h" #include "switch/kernel/thread.h" #include "switch/kernel/semaphore.h" +#include "switch/kernel/barrier.h" #include "switch/kernel/virtmem.h" #include "switch/kernel/detect.h" #include "switch/kernel/random.h" @@ -76,6 +77,7 @@ extern "C" { #include "switch/runtime/nxlink.h" #include "switch/runtime/util/utf.h" +#include "switch/runtime/util/list.h" #include "switch/runtime/devices/console.h" #include "switch/runtime/devices/usb_comms.h" diff --git a/nx/include/switch/kernel/barrier.h b/nx/include/switch/kernel/barrier.h new file mode 100644 index 00000000..f00f6dc3 --- /dev/null +++ b/nx/include/switch/kernel/barrier.h @@ -0,0 +1,49 @@ +/** + * @file barrier.h + * @brief Barrier synchronization primitive. + * @author Yordrar + * @copyright libnx Authors + */ +#pragma once +#include "condvar.h" +#include "mutex.h" +#include "thread.h" +#include "../runtime/util/list.h" + +typedef struct barrier { + List threads_registered; + List threads_waiting; + Mutex mutex; +} Barrier; + +/** + * @brief Allocates memory for a barrier + * @param b Barrier object + */ +void barrierInit(Barrier* b); + +/** + * @brief Frees the memory allocated for a barrier + * @param b Barrier object + */ +void barrierFree(Barrier* b); + +/** + * @brief Registers a thread that is going to use the specified barrier + * @param b Barrier object + * @param thread The thread to register + */ +void barrierRegister(Barrier* b, Thread* thread); + +/** + * @brief Unegisters a thread that is not going to use anymore the specified barrier + * @param b Barrier object + * @param thread The thread to unregister + */ +void barrierUnregister(Barrier* b, Thread* thread); + +/** + * @brief Waits until all processes registered in the barrier call this function, if a not registered thread calls this function, it returns without waiting + * @param b Barrier object + */ +void barrierWait(Barrier* b); diff --git a/nx/include/switch/runtime/util/list.h b/nx/include/switch/runtime/util/list.h new file mode 100644 index 00000000..75298770 --- /dev/null +++ b/nx/include/switch/runtime/util/list.h @@ -0,0 +1,135 @@ +/** + * @file list.h + * @brief Singly Linked List data structure. + * @author Yordrar + * @copyright libnx Authors + */ +#pragma once +#include "../../types.h" +#include "../../kernel/condvar.h" +#include "../../kernel/rwlock.h" +#include "../../kernel/thread.h" + +typedef struct node { + void* item; + struct node* next; +} Node; + +typedef struct list { + Node* header; + Node* last; + u32 num_nodes; + RwLock mutex; +} List; + +/** + * @brief Allocates memory for a list + * @param l List object + */ +void listInit(List* l); + +/** + * @brief Frees memory allocated for a list + * @param l List object + */ +void listFree(List* l); + +/** + * @brief Inserts something in a position + * @param l List object + * @param item A pointer to the thing you want to insert + * @param pos The position to insert (0 is the first position) + */ +void listInsert(List* l, void* item, u32 pos); + +/** + * @brief Inserts something in the first position + * @param l List object + * @param item A pointer to the thing you want to insert + */ +static inline void listInsertFirst(List* l, void* item) { + listInsert(l, item, 0); +} + +/** + * @brief Inserts something at the end of the list + * @param l List object + * @param item A pointer to the thing you want to insert + */ +void listInsertLast(List* l, void* item); + +/** + * @brief Deletes the node of the list which has the item specified (makes a pointer comparison to ckeck that) + * @param l List object + * @param item A pointer to the thing you want to delete + */ +void listDelete(List* l, void* item); + +/** + * @brief Checks if the item is inserted in the list (makes a pointer comparison to ckeck that) + * @param l List object + * @param item A pointer to the thing you want to check + * @return true if the item is in the list, false otherwise + */ +bool listIsInserted(List* l, void* item); + +/** + * @brief Returns the number of items inserted in the list + * @param l List object + * @return The number of nodes (the number of inserted things) in the list + */ +u32 listGetNumNodes(List* l); + +/** + * @brief Returns the item inserted in an specified position + * @param l List object + * @param pos The position of the item + * @return A pointer to that item, NULL if it isn't found + */ +void* listGetItem(List* l, u32 pos); + +/** + * @brief Inserts the item in the first position + * @param l List object + * @param item A pointer to something to store + */ +static inline void listPushFront(List* l, void* item) { + listInsert(l, item, 0); +} + +/** + * @brief Returns the item inserted in the first position and deletes it + * @param l List object + * @return A pointer to that item, NULL if the list is empty + */ +void* listPopFront(List* l); + +/** + * @brief Returns the item inserted in the first position + * @param l List object. + * @return A pointer to that item, NULL if the list is empty + */ +void* listPeekFront(List* l); + +/** + * @brief Inserts the item in the last position + * @param l List object. + * @param item A pointer to something you want to insert + */ +static inline void listPushBack(List* l, void* item) { + listInsertLast(l, item); +} + +/** + * @brief Returns the item inserted in the last position and deletes it + * @param l List object. + * @return A pointer to that item, NULL if the list is empty + */ +void* listPopBack(List* l); + +/** + * @brief Returns the item inserted in the last position + * @param l List object. + * @return A pointer to that item, NULL if the list is empty + */ +void* listPeekBack(List* l); \ No newline at end of file diff --git a/nx/source/kernel/barrier.c b/nx/source/kernel/barrier.c new file mode 100644 index 00000000..fda1b463 --- /dev/null +++ b/nx/source/kernel/barrier.c @@ -0,0 +1,50 @@ +#include "kernel/barrier.h" +#include "../internal.h" + +void barrierInit(Barrier* b) { + mutexInit(&b->mutex); + mutexLock(&b->mutex); + listInit(&b->threads_registered); + listInit(&b->threads_waiting); + mutexUnlock(&b->mutex); +} + +void barrierFree(Barrier* b) { + mutexLock(&b->mutex); + listFree(&b->threads_registered); + listFree(&b->threads_waiting); + mutexUnlock(&b->mutex); +} + +void barrierRegister(Barrier* b, Thread* thread) { + if(listIsInserted(&b->threads_registered, (void*)thread)) { + return; + } + listInsertLast(&b->threads_registered, (void*)thread); +} + +void barrierUnregister(Barrier* b, Thread* thread) { + listDelete(&b->threads_registered, (void*)thread); +} + +void barrierWait(Barrier* b) { + Thread* thread = getThreadVars()->thread_ptr; + if(!listIsInserted(&b->threads_registered, (void*)thread)) { + return; + } + + mutexLock(&b->mutex); + if(listGetNumNodes(&b->threads_registered) == listGetNumNodes(&b->threads_waiting)+1) { + while(listGetNumNodes(&b->threads_waiting) > 0) { + Thread* current_thread = listGetItem(&b->threads_waiting, 0); + svcSetThreadActivity(current_thread->handle, 0); + listDelete(&b->threads_waiting, (void*)current_thread); + } + mutexUnlock(&b->mutex); + } + else { + listInsertLast(&b->threads_waiting, (void*)thread); + mutexUnlock(&b->mutex); + svcSleepThread(0); + } +} \ No newline at end of file diff --git a/nx/source/runtime/util/list.c b/nx/source/runtime/util/list.c new file mode 100644 index 00000000..49b1f25f --- /dev/null +++ b/nx/source/runtime/util/list.c @@ -0,0 +1,172 @@ +#include "runtime/util/list.h" +#include + +void listInit(List* l) { + rwlockInit(&l->mutex); + rwlockWriteLock(&l->mutex); + Node* header = (Node*)malloc(sizeof(Node)); + header->item = NULL; + header->next = NULL; + + l->header = header; + l->last = header; + l->num_nodes = 0; + rwlockWriteUnlock(&l->mutex); +} + +void listFree(List* l) { + rwlockWriteLock(&l->mutex); + Node* aux = l->header; + while(aux != NULL) { + Node* erase = aux; + aux = aux->next; + free(erase); + } + l->header = NULL; + l->last = NULL; + l->num_nodes = 0; + rwlockWriteUnlock(&l->mutex); +} + +void listInsert(List* l, void* item, u32 pos) { + rwlockReadLock(&l->mutex); + if(pos > l->num_nodes || pos < 0) { + return; + } + rwlockReadUnlock(&l->mutex); + + rwlockWriteLock(&l->mutex); + Node* aux = l->header; + for(u32 i = pos; i > 0; i--) { + aux = aux->next; + } + Node* new = (Node*)malloc(sizeof(Node)); + new->item = item; + new->next = aux->next; + aux->next = new; + + if(pos == l->num_nodes) { + l->last = new; + } + + l->num_nodes++; + rwlockWriteUnlock(&l->mutex); +} + +void listInsertLast(List* l, void* item) { + rwlockWriteLock(&l->mutex); + Node* new = (Node*)malloc(sizeof(Node)); + new->item = item; + new->next = NULL; + l->last->next = new; + l->last = new; + rwlockWriteUnlock(&l->mutex); +} + +void listDelete(List* l, void* item) { + rwlockWriteLock(&l->mutex); + + Node* aux = l->header; + while(aux->next != NULL && aux->next->item != item) { + aux = aux->next; + } + + if(aux->next != NULL && aux->next->item == item) { + Node* delete = aux->next; + aux->next = delete->next; + free(delete); + l->num_nodes--; + } + + rwlockWriteUnlock(&l->mutex); +} + +void listDeleteAtPos(List* l, u32 pos) { + rwlockReadLock(&l->mutex); + if(pos >= l->num_nodes || pos < 0) { + return; + } + Node* aux = l->header; + for(int i = pos; i > 0; i++) { + aux = aux->next; + } + Node* delete = aux->next; + aux->next = delete->next; + free(delete); + l->num_nodes--; + rwlockReadUnlock(&l->mutex); +} + +bool listIsInserted(List* l, void* item) { + rwlockReadLock(&l->mutex); + Node* aux = l->header; + while(aux != NULL && aux->item != item) { + aux = aux->next; + } + bool result = aux == NULL ? false : true; + rwlockReadUnlock(&l->mutex); + return result; +} + +u32 listGetNumNodes(List* l) { + rwlockReadLock(&l->mutex); + u32 result = l->num_nodes; + rwlockReadUnlock(&l->mutex); + return result; +} + +void* listGetItem(List* l, u32 pos) { + rwlockReadLock(&l->mutex); + if(pos >= l->num_nodes || pos < 0) { + return NULL; + } + Node* aux = l->header->next; + for(u32 i = pos; i > 0; i--) { + aux = aux->next; + } + void* result = aux->item; + rwlockReadUnlock(&l->mutex); + return result; +} + +void* listPopFront(List* l) { + rwlockReadLock(&l->mutex); + if(l->num_nodes == 0) { + return NULL; + } + void* result = listGetItem(l, 0); + listDeleteAtPos(l, 0); + rwlockReadUnlock(&l->mutex); + return result; +} + +void* listPeekFront(List* l) { + rwlockReadLock(&l->mutex); + if(l->num_nodes == 0) { + return NULL; + } + void* result = listGetItem(l, 0); + rwlockReadUnlock(&l->mutex); + return result; +} + +void* listPopBack(List* l) { + rwlockReadLock(&l->mutex); + if(l->num_nodes == 0) { + return NULL; + } + void* result = listGetItem(l, l->num_nodes-1); + listDeleteAtPos(l, l->num_nodes-1); + rwlockReadUnlock(&l->mutex); + return result; +} + +void* listPeekBack(List* l) { + rwlockReadLock(&l->mutex); + if(l->num_nodes == 0) { + return NULL; + } + void* result = listGetItem(l, l->num_nodes-1); + rwlockReadUnlock(&l->mutex); + return result; +} \ No newline at end of file