Homebrew Question Any C coders in here?

mrdude

Developer
OP
Developer
Joined
Dec 11, 2015
Messages
3,071
Trophies
1
Age
56
XP
8,227
Currently I want to add some code that will run a task every x seconds, without using a delay I tried this:

Code:
        u32 runningtime = get_tmr_us() / 1000000;
        int delay = 5; // delay in seconds
        int total = runningtime + delay;
       
        while (((u32) get_tmr_us() / 1000000) < total)
        {
            //do nothing
        }

//code runs here after 5 seconds

The code above works (well it waits 5 seconds), then the next code is run - however it freezes the rest of the app from running while it's looping. Is there another way to do the same thing but without freezing the app? (Using a delay/sleep also freezes the app).

This is for nintendo switch payload, also before someone says 'look at hekate' - etc, I already did, but that doesn't help. I just need a short example and a different approach, I'm not a C coder - so that's why I don't know another way to do this.
 
  • Like
Reactions: Mythical

rrocha

Developer
Developer
Joined
Nov 21, 2016
Messages
137
Trophies
0
XP
1,813
Country
Portugal
This is not C specific. You're blocking the UI thread. Simple applications can have their code on the same thread as the UI but if you're doing any type of heavy-weight operations or something that can block the UI (like sleeping/halting execution for a certain amount of time), you should offload those routines to a background thread. By going multi-threaded, your code will become more complex and you'll need to also implement a form of communication between threads (as I'm assuming your UI will react to changes made on your code over there). Get familiarised with these concepts first and do some prototyping / sample code before continuing your initial implementation. Good luck with your project :)
 

mrdude

Developer
OP
Developer
Joined
Dec 11, 2015
Messages
3,071
Trophies
1
Age
56
XP
8,227
This is not C specific. You're blocking the UI thread. Simple applications can have their code on the same thread as the UI but if you're doing any type of heavy-weight operations or something that can block the UI (like sleeping/halting execution for a certain amount of time), you should offload those routines to a background thread. By going multi-threaded, your code will become more complex and you'll need to also implement a form of communication between threads (as I'm assuming your UI will react to changes made on your code over there). Get familiarised with these concepts first and do some prototyping / sample code before continuing your initial implementation. Good luck with your project :)

OK thanks for the reply, that's a bit complex for what I need, I have a workaround but I need to touch the screen to update - not an issue really, but I was wondering how you 'delay' a function - without freezing the app. You explained well though so I'll google some of that info for future reference.

Cheers :-)
 

YamiZee

Well-Known Member
Member
Joined
Aug 18, 2013
Messages
264
Trophies
0
Age
28
XP
1,310
Country
Finland
If you're in a loop why can't you just mark the time and every iteration check if the time has elapsed and then run your code? I'm not sure why you need to sleep the thread.
 

mrdude

Developer
OP
Developer
Joined
Dec 11, 2015
Messages
3,071
Trophies
1
Age
56
XP
8,227
If you're in a loop why can't you just mark the time and every iteration check if the time has elapsed and then run your code? I'm not sure why you need to sleep the thread.

That's what the code I posted above does, but because it's in a loop - it causes the app to freeze until the loop ends.
 

YamiZee

Well-Known Member
Member
Joined
Aug 18, 2013
Messages
264
Trophies
0
Age
28
XP
1,310
Country
Finland
Oh I see. But can't you use a variable somewhere? To mark the time then run all of the app code and then come back every frame to check on the time? Instead of a loop.

--------------------- MERGED ---------------------------

//save this one where it doesn't die and then access it from here every iteration without overwriting it. only set it once and never again
u32 runningtime = get_tmr_us() / 1000000;
//run this code every iteration
int delay = 5; // delay in seconds
int total = runningtime + delay;

if (((u32) get_tmr_us() / 1000000) >= total)
{
//code runs here after 5 seconds
}
 

mrdude

Developer
OP
Developer
Joined
Dec 11, 2015
Messages
3,071
Trophies
1
Age
56
XP
8,227
I already tried that way, but I couldn't get it to work.

See screenshot:
9WRdPRd.jpg


When I touch the screen on the battery icon, the battery charge level gets shown and updated, also the battery icon bars change colour when the charge level drops - but you need to touch the screen for this to update. It's not a big deal really as most people don't use that screen for long without touching it - but it's kind of annoying none the less. I'm kind of tired trying to get that to work now, but that's life I guess :-)
 
Last edited by mrdude,
  • Like
Reactions: Jay Griggs

hippy dave

BBMB
Member
Joined
Apr 30, 2012
Messages
9,883
Trophies
2
XP
29,267
Country
United Kingdom
When I touch the screen on the battery icon, the battery charge level gets shown and updated, also the battery icon bars change colour when the charge level drops - but you need to touch the screen for this to update. It's not a big deal really as most people don't use that screen for long without touching it - but it's kind of annoying none the less. I'm kind of tired trying to get that to work now, but that's life I guess :-)
It sounds like Argon's existing code only updates the screen when something is touched, which probably makes sense if it wasn't providing any live-changing info before. Look at the code for where it processes a touch event, and see what it does to update the screen. Do that in your code that you use to change values after a delay.
 
  • Like
Reactions: mrdude

mrdude

Developer
OP
Developer
Joined
Dec 11, 2015
Messages
3,071
Trophies
1
Age
56
XP
8,227
It sounds like Argon's existing code only updates the screen when something is touched, which probably makes sense if it wasn't providing any live-changing info before. Look at the code for where it processes a touch event, and see what it does to update the screen. Do that in your code that you use to change values after a delay.

Yes, thanks - I've already checked all that code - and put in the call to the update the icon function in all touchscreen functions (just for testing), but you still need to touch the screen for it to update. The screen also updates when you touch it - so I just put the code in there, just after when the icons are drawn on the screen.

When I post the updated argonnx-mod, I'll included the modded code, that way someone else can have a look and see if they can improve it (which won't be hard after my feeble attempts at coding).
 
D

Deleted User

Guest
When I was learning, I had a counter for time in millis().

Set the timer when called, check to see if it's 5000 above the time that was set.

Not sure if that'll help you for what you need, but that's what I was doing when learning C.
 

mrdude

Developer
OP
Developer
Joined
Dec 11, 2015
Messages
3,071
Trophies
1
Age
56
XP
8,227
When I was learning, I had a counter for time in millis().

Set the timer when called, check to see if it's 5000 above the time that was set.

Not sure if that'll help you for what you need, but that's what I was doing when learning C.

That's what the code above is doing, but you still need to check to see what the counter is at - that's why I put a while loop in the code, to check the counter. But the loop is causing the issue, I don't know what other way to check the counter value, not without looping.
 
D

Deleted User

Guest
That's what the code above is doing, but you still need to check to see what the counter is at - that's why I put a while loop in the code, to check the counter. But the loop is causing the issue, I don't know what other way to check the counter value, not without looping.
DERP! My apologies, I didn't read the code completely and thought it was using a different method. It's also been a little since I've looked at C, as I'm learning C#, Java, Python and Javascript all at once for college.

Sorry I can't help! Not there yet :P
 

Deleted member 383179

Well-Known Member
Newcomer
Joined
Feb 12, 2016
Messages
62
Trophies
0
XP
995
How complicated is the code you need to run each 5 seconds? What is it?

If it's a simple or fast routine I'd probably write this (psuedocode) like
[main loop]
if elapsedtime > 5 seconds then (whatever code you needed to run), bring elapsedtime down 5 seconds
[end main loop]
so that it runs everytime more than 5 seconds have passed, but if it's slow it will still block.
If it runs too slowly to run in-loop, you need to look at threading.
Code:
//example
while (runMainLoop){
    //whatever your current code is
    //essentially, the below code will only run once per 5 seconds, but it still runs within the main loop.
    if (time - starttime > 5000){ //5000 is whatever duration 5 seconds is, might be 5000000
        //your time-triggered code
        starttime = get_tmr_us(); //or whatever it was to get the current time.
    }
}
 
Last edited by Deleted member 383179,

mrdude

Developer
OP
Developer
Joined
Dec 11, 2015
Messages
3,071
Trophies
1
Age
56
XP
8,227
How complicated is the code you need to run each 5 seconds? What is it?

The code is fairly simple, it just prints out the battery percentage:

Code:
int battery_info() {
   
        u32 battPercent = 0; 
        max17050_get_property(MAX17050_RepSOC, (int *)&battPercent);
        gfx_con_init(&g_gfx_con, &g_gfx_ctxt);
        g_gfx_con.scale = 2;

        int per1 = (battPercent >> 8) & 0xFF;
        int per2 = (battPercent & 0xFF) / 25.5001; //exact to 4 decimal places
       
        gfx_con_setpos(&g_gfx_con, 14, 165);
       
        if (per2 >= 0)
            per1 = per1 + 1; // keep value the same as the switch main screen
       
        /*
        // this delay is freeizing the app
        u32 runningtime = get_tmr_us() / 1000000;
        int delay = 5; // delay in seconds
        int total = runningtime + delay;
       
        while (((u32) get_tmr_us() / 1000000) < total)
        {
            //failsafe
            if (((u32)get_tmr_us() / 1000000) > total)
                break;
        }
        */
       
       
    if (per1 < 20){
          gfx_printf(&g_gfx_con, "%kBattery: %k%d%% %k", 0xFF3065E1, colour_red, per1, 0xFFCCCCCC); //use %% to show percentage
              } else {
                      gfx_printf(&g_gfx_con, "%kBattery: %k%d%% %k", 0xFF3065E1, GREEN, per1, 0xFFCCCCCC); //use %% to show percentage
              }
           
        return 0;
}

I just want to call that code every 5 seconds to update automatically - without needing to touch the screen.
 

Deleted member 383179

Well-Known Member
Newcomer
Joined
Feb 12, 2016
Messages
62
Trophies
0
XP
995
Then yeah, you can just call that function from the main code with an if statement that checks the time.
Code:
//put this somewhere, ideally make constant and only run/set it once
u32 delay = 5000000; //5 seconds
//main
while (runMainLoop){
    //rest of your app's code
    if (((u32) get_tmr_us() - starttime > delay){ //wait for elapsed time to be greater than [delay] microseconds or whatever unit this is
        battery_info(); //display your info
        starttime = get_tmr_us(); //"reset" the wait by setting timer to current time
    }
}

Also, you could probably make battery_info() return void instead of an int unless you have plans for the int.
 
Last edited by Deleted member 383179,
  • Like
Reactions: mrdude

mrdude

Developer
OP
Developer
Joined
Dec 11, 2015
Messages
3,071
Trophies
1
Age
56
XP
8,227
Then yeah, you can just call that function from the main code with an if statement that checks the time.
Code:
//put this somewhere, ideally make constant and only run/set it once
u32 delay = 5000000; //5 seconds
//main
while (runMainLoop){
    //rest of your app's code
    if (((u32) get_tmr_us() - starttime > delay){ //wait for elapsed time to be greater than [delay] microseconds or whatever unit this is
        battery_info(); //display your info
        starttime = get_tmr_us(); //"reset" the wait by setting timer to current time
    }
}

Also, you could probably make battery_info() return void instead of an int unless you have plans for the int.

I've already tried calling from the main void, the problem with that is that - once the menu is drawn in the buffer, the clock info gets over written and is not shown on screen anymore, if you have a look at argon-nx code you can see that.

https://github.com/Guillem96/argon-nx

I think the main loop is in main.c, and uses void ipl_main() in a constant loop ((like the main void in Arduino)although I'm not 100% sure about that).

https://github.com/Guillem96/argon-nx/blob/master/src/main.c

Edit, I tried putting this in the main.c:
Code:
while (ipl_main()){
          //put this somewhere, ideally make constant and only run/set it once
           u32 delay = 5000000; //5 seconds
           u32 starttime = get_tmr_us(); //get how long the system has been running

           if (((u32) get_tmr_us() - starttime) > delay){ //wait for elapsed time to be greater than [delay] microseconds or whatever unit this is
                  battery_info(); //display your info
                  u32 starttime = get_tmr_us(); //"reset" the wait by setting timer to current time
            }
}

If you put above where the menu is loaded, the menu never loads as the app is still stuck in a loop, if you put under where the menu is loaded - it never runs. If you put in the menu - the menu loads, but freezes the app - once again the app is stuck in the loop. I think multithreading is the only solution - but requires too much code to be changed due to other timers set in the original code, so I'll abandon this for now. Thanks for all the input though. It's been helpful.
 
Last edited by mrdude,

JustBrandonT

Well-Known Member
Newcomer
Joined
Mar 11, 2018
Messages
75
Trophies
0
Age
34
XP
518
Country
Canada
libnx supports threading so you can use pthreads.. First create a thread and a lock (preferably a mutex or counting lock like a semaphore)..

Then run your code on a separate thread while your main thread does "stuff" like updating the UI or w/e..
When you want to notify the main-thread that your task is complete, you can either use a QUEUE like I did below or you can use a condition-variable..
I can't teach you EVERYTHING about multi-threading.. but the below should be a start..

OLD CODE:
Code:
//
//  main.c
//  Threading Example
//
//  Created by Brandon T on 2019-04-15.
//  Copyright © 2019 SO. All rights reserved.
//

//Switch Brew
//https://github.com/switchbrew/libnx/blob/master/nx/include/switch/kernel/thread.h

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

typedef struct Task
{
    void(*func)(void);
    struct Task *next;
} Task;

typedef struct Queue
{
    int size;
    Task *head;
    Task *tail;
} Queue;

Queue* createQueue()
{
    Queue *queue = malloc(sizeof(Queue));
    queue->head = NULL;
    queue->tail = NULL;
    queue->size = 0;
    return queue;
}

void destroyQueue(Queue **queue)
{
    if (queue && *queue)
    {
        Task *task = (*queue)->head;
        while (task)
        {
            Task *temp = task;
            task = task->next;
            free(temp);
        }
    }
    free(*queue);
    *queue = NULL;
}

void enqueue(Queue *queue, void(*func)(void))
{
    Task *task = malloc(sizeof(Task));
    task->func = func;
    task->next = NULL;
 
    if (queue->size == 0)
    {
        queue->head = queue->tail = task;
    }
    else
    {
        queue->tail->next = task;
        queue->tail = task;
    }
 
    ++queue->size;
}

void dequeue(Queue *queue)
{
    if (queue->size > 0)
    {
        Task *task = queue->head;
        if (queue->size > 1)
        {
            queue->head = queue->head->next;
        }
        else
        {
            queue->head = NULL;
            queue->tail = NULL;
        }
 
        --queue->size;
        free(task);
    }
}

void (*peek(Queue *queue))(void)
{
    if (queue->size > 0)
    {
        Task *task = queue->head;
        return task->func;
    }
    return NULL;
}

int size(Queue *queue)
{
    return queue->size;
}

void task() {
    printf("Running on main thread: %s\n", pthread_main_np() != 0 ? "true" : "false");
}

void* thread_func(void *args)
{
    sleep(5);
    printf("Running on main thread: %s\n", pthread_main_np() != 0 ? "true" : "false");
 
    void** arguments = (void**)args;
    Queue *mainQueue = (Queue *)arguments[0];
    int *isRunning = (int *)arguments[1];
    pthread_mutex_t *mutex = arguments[2];
    pthread_mutex_lock(mutex);
 
    enqueue(mainQueue, task);
 
    *isRunning = 0;
    pthread_mutex_unlock(mutex);
    //pthread_exit(NULL);
    return NULL;
}

int main(int argc, const char * argv[]) {
 
    Queue *mainQueue = createQueue();
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 
    pthread_t thread_id = 0;
    int isRunning = 1;
    void* args[] = {mainQueue, &isRunning, &mutex};
    pthread_create(&thread_id, NULL, thread_func, args);
 
    while(1)
    {
        pthread_mutex_lock(&mutex);
 
        if (size(mainQueue) > 0)
        {
            void(*func)(void) = peek(mainQueue);
            dequeue(mainQueue);
            func();
        }
 
        int running = isRunning;
        pthread_mutex_unlock(&mutex);
 
        if (!running)
        {
            break;
        }
        usleep(10); //because infinite loop continuously can cause high CPU usage..
    }
 
    pthread_join(thread_id, NULL);
    destroyQueue(&mainQueue);

    return 0;
}




NEW CODE:

For real multi-threading code, you want to use condition variables.. so let's start with that..

First the Queue:

Queue.h:

Code:
//
//  Queue.h
//  Threading Example
//
//  Created by Brandon T on 2019-04-15.
//  Copyright © 2019 SO. All rights reserved.
//

#ifndef Queue_h
#define Queue_h

#include <stdint.h>

typedef struct Task
{
    void(*func)(void);
    struct Task *next;
} Task;

typedef struct Queue Queue;

Queue* createQueue(void);
void destroyQueue(Queue **queue);
void enqueue(Queue *queue, void(*func)(void));
void dequeue(Queue *queue);
void (*peek(Queue *queue))(void);
size_t size(Queue *queue);

#endif /* Queue_h */

Queue.c:
Code:
//
//  Queue.c
//  Threading Example
//
//  Created by Brandon T on 2019-04-15.
//  Copyright © 2019 SO. All rights reserved.
//

#include "Queue.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct Queue
{
    size_t size;
    Task *head;
    Task *tail;
} Queue;

Queue* createQueue(void)
{
    Queue *queue = malloc(sizeof(Queue));
    queue->head = NULL;
    queue->tail = NULL;
    queue->size = 0;
    return queue;
}

void destroyQueue(Queue **queue)
{
    if (queue && *queue)
    {
        Task *task = (*queue)->head;
        while (task)
        {
            Task *temp = task;
            task = task->next;
            free(temp);
        }
    }
    free(*queue);
    *queue = NULL;
}

void enqueue(Queue *queue, void(*func)(void))
{
    Task *task = malloc(sizeof(Task));
    task->func = func;
    task->next = NULL;
 
    if (queue->size == 0)
    {
        queue->head = queue->tail = task;
    }
    else
    {
        queue->tail->next = task;
        queue->tail = task;
    }
 
    ++queue->size;
}

void dequeue(Queue *queue)
{
    if (queue->size > 0)
    {
        Task *task = queue->head;
        if (queue->size > 1)
        {
            queue->head = queue->head->next;
        }
        else
        {
            queue->head = NULL;
            queue->tail = NULL;
        }
 
        --queue->size;
        free(task);
    }
}

void (*peek(Queue *queue))(void)
{
    if (queue->size > 0)
    {
        Task *task = queue->head;
        return task->func;
    }
    return NULL;
}

size_t size(Queue *queue)
{
    return queue->size;
}

And now for Thread-Group:

ThreadPool.h:
Code:
//
//  ThreadPool.h
//  Threading Example
//
//  Created by Brandon T on 2019-04-15.
//  Copyright © 2019 SO. All rights reserved.
//

#ifndef ThreadPool_h
#define ThreadPool_h

#include <stdbool.h>
#include <stdint.h>
#include "Queue.h"

typedef struct ThreadPool ThreadPool;

ThreadPool* createThreadPool(size_t num_threads);
void destroyThreadPool(ThreadPool** pool);
bool threadEnqueue(ThreadPool* pool, void(*func)(void));
size_t threadTaskCount(ThreadPool* pool);
void threadWaitUntilFinished(ThreadPool* pool);

#endif /* ThreadPool_h */

ThreadPool.c:
Code:
//
//  ThreadPool.c
//  Threading Example
//
//  Created by Brandon T on 2019-04-15.
//  Copyright © 2019 SO. All rights reserved.
//

#include "ThreadPool.h"

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>

typedef struct ThreadPool {
    pthread_t *threads;
    size_t num_threads;
    pthread_mutex_t mutex;
    pthread_cond_t condition;
    Queue *queue;
    bool stop;
 
    pthread_mutex_t listener_mutex;
    pthread_cond_t listener_condition;
} ThreadPool;


void* thread_pool_func(void* args)
{
    ThreadPool* pool = (ThreadPool *)args;
 
    //Keep checking for tasks..
    while(true)
    {
        pthread_mutex_lock(&pool->mutex);
 
        //While the queue has tasks and the pool is not stopped, we keep waiting..
        while (!(pool->stop || size(pool->queue) > 0))
        {
            pthread_cond_wait(&pool->condition, &pool->mutex);
        }
 
        //If the pool is stopped or the queue is empty, exit the thread..
        if (pool->stop && size(pool->queue) <= 0)
        {
            pthread_mutex_unlock(&pool->mutex);
            pthread_exit(NULL);
            break;
        }
 
        //Execute pending tasks..
        void(*task)(void) = peek(pool->queue);
        if (task)
        {
            dequeue(pool->queue);
            pthread_mutex_unlock(&pool->mutex);
            task();
 
            pthread_cond_signal(&pool->listener_condition);
        }
        else
        {
            pthread_mutex_unlock(&pool->mutex);
        }
    }
 
    pthread_exit(NULL);
    return NULL;
}

ThreadPool* createThreadPool(size_t num_threads)
{
    //Allocate a thread pool..
    ThreadPool *pool = malloc(sizeof(ThreadPool));
 
    //Allocate space for threads..
    pool->threads = malloc(sizeof(pthread_t) * num_threads);
    pool->num_threads = num_threads;
    pool->stop = false;
 
    //Initialize the pool..
    pthread_mutex_init(&pool->mutex, NULL);
    pthread_cond_init(&pool->condition, NULL);
 
    pthread_mutex_init(&pool->listener_mutex, NULL);
    pthread_cond_init(&pool->listener_condition, NULL);
    pool->queue = createQueue();
 
    //Create all the worker threads..
    for (size_t i = 0; i < num_threads; ++i)
    {
        pthread_create(&pool->threads[i], NULL, thread_pool_func, pool);
    }
 
    return pool;
}

void destroyThreadPool(ThreadPool** pool)
{
    if (pool && *pool)
    {
        //Stop all threads..
        pthread_mutex_lock(&(*pool)->mutex);
        (*pool)->stop = true;
        pthread_mutex_unlock(&(*pool)->mutex);
        pthread_cond_broadcast(&(*pool)->condition);
 
        //Let them cleanup/finish their jobs..
        for (size_t i = 0; i < (*pool)->num_threads; ++i)
        {
            pthread_join((*pool)->threads[i], NULL);
        }
 
        //Clean up..
        destroyQueue(&(*pool)->queue);
        pthread_cond_destroy(&(*pool)->condition);
        pthread_mutex_destroy(&(*pool)->mutex);
 
 
        pthread_cond_broadcast(&(*pool)->listener_condition);
        pthread_cond_destroy(&(*pool)->listener_condition);
        pthread_mutex_destroy(&(*pool)->listener_mutex);
 
        free(*pool);
        *pool = NULL;
    }
}

bool threadEnqueue(ThreadPool* pool, void(*func)(void))
{
    pthread_mutex_lock(&pool->mutex);
    if (pool->stop)
    {
        //cannot call enqueue on a stopped thread-pool..
        pthread_mutex_unlock(&pool->mutex);
        return false;
    }
 
    //Add a task to the queue..
    enqueue(pool->queue, func);
 
    //Signal to one of the threads to start executing the task..
    pthread_mutex_unlock(&pool->mutex);
    pthread_cond_signal(&pool->condition);
    return true;
}

size_t threadTaskCount(ThreadPool* pool)
{
    return size(pool->queue);
}

void threadWaitUntilFinished(ThreadPool* pool)
{
    pthread_mutex_lock(&pool->listener_mutex);
    while(size(pool->queue) > 0)
    {
        pthread_cond_wait(&pool->listener_condition, &pool->listener_mutex);
    }
    pthread_mutex_unlock(&pool->listener_mutex);
}



Usage:

main.c:
Code:
//
//  main.c
//  Threading Example
//
//  Created by Brandon T on 2019-04-15.
//  Copyright © 2019 SO. All rights reserved.
//

//Switch Brew
//https://github.com/switchbrew/libnx/blob/master/nx/include/switch/kernel/thread.h

//NOTE: SOME of these headers you do NOT need.. I am importing them so that this example runs.. but in your Nintendo-Switch code, you may not need them.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>

#include "Queue.h"
#include "ThreadPool.h"

void sleep_millis(float milliseconds)
{
    usleep((useconds_t)(1000000 * milliseconds));
}

void task()
{
    //This is where you'd put your code to wait X amount of time.. then execute..
    sleep_millis(1); //Simulating heavy calculations/waiting..

    //Finished waiting.. let's execute some code..
    static int i = 0;
    printf("Finished Task %d..\n", ++i);
}

int main(int argc, const char * argv[]) {
    ThreadPool* thread_pool = createThreadPool(10);  //Create 10 threads.. You probably only need one though.. depends what you're doing..
 
    for (int i = 0; i < 1000; ++i)   //create 1000 tasks..
    {
        threadEnqueue(thread_pool, task);  //let the pool take care of them all..
        printf("Started Task: %d\n", i);
    }
 
    //Have the main thread sleep nicely using a condition variable until the pool is finished..
    //NOTE: You probably don't want the main-thread to wait so you can just remove the line below..
    threadWaitUntilFinished(thread_pool);  //You can just continue updating your UI here or do whatever you want instead of making the main thread-wait..
    printf("Finished");
 
    destroyThreadPool(&thread_pool); //Join all the threads and cleanup..

    return 0;
}

Both approaches will work for you.. either just queueing + polling with a loop.. OR using a condition variable to get "notified" when stuff is completed so you don't loop crazy..
 
Last edited by JustBrandonT,

Lucifer666

all the world needs is me
Member
Joined
Apr 22, 2011
Messages
1,626
Trophies
1
Location
The Fourth Dimension
XP
2,160
Country
United Kingdom
libnx supports threading so you can use pthreads.. First create a thread and a lock (preferably a mutex or counting lock like a semaphore)..

Then run your code on a separate thread while your main thread does "stuff" like updating the UI or w/e..
When you want to notify the main-thread that your task is complete, you can either use a QUEUE like I did below or you can use a condition-variable..
I can't teach you EVERYTHING about multi-threading.. but the below should be a start..

Code:
//
//  main.c
//  Threading Example
//
//  Created by Brandon T on 2019-04-15.
//  Copyright © 2019 SO. All rights reserved.
//

//Switch Brew
//https://github.com/switchbrew/libnx/blob/master/nx/include/switch/kernel/thread.h

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

typedef struct Task
{
    void(*func)(void);
    struct Task *next;
} Task;

typedef struct Queue
{
    int size;
    Task *head;
    Task *tail;
} Queue;

Queue* createQueue()
{
    Queue *queue = malloc(sizeof(Queue));
    queue->head = NULL;
    queue->tail = NULL;
    queue->size = 0;
    return queue;
}

void destroyQueue(Queue **queue)
{
    if (queue && *queue)
    {
        Task *task = (*queue)->head;
        while (task)
        {
            Task *temp = task;
            task = task->next;
            free(temp);
        }
    }
    free(*queue);
    *queue = NULL;
}

void enqueue(Queue *queue, void(*func)(void))
{
    Task *task = malloc(sizeof(Task));
    task->func = func;
    task->next = NULL;
 
    if (queue->size == 0)
    {
        queue->head = queue->tail = task;
    }
    else
    {
        queue->tail->next = task;
        queue->tail = task;
    }
 
    ++queue->size;
}

void dequeue(Queue *queue)
{
    if (queue->size > 0)
    {
        Task *task = queue->head;
        if (queue->size > 1)
        {
            queue->head = queue->head->next;
        }
        else
        {
            queue->head = NULL;
            queue->tail = NULL;
        }
    
        --queue->size;
        free(task);
    }
}

void (*peek(Queue *queue))(void)
{
    if (queue->size > 0)
    {
        Task *task = queue->head;
        return task->func;
    }
    return NULL;
}

int size(Queue *queue)
{
    return queue->size;
}

void task() {
    printf("Running on main thread: %s\n", pthread_main_np() != 0 ? "true" : "false");
}

void* thread_func(void *args)
{
    sleep(5);
    printf("Running on main thread: %s\n", pthread_main_np() != 0 ? "true" : "false");
 
    void** arguments = (void**)args;
    Queue *mainQueue = (Queue *)arguments[0];
    int *isRunning = (int *)arguments[1];
    pthread_mutex_t *mutex = arguments[2];
    pthread_mutex_lock(mutex);
 
    enqueue(mainQueue, task);
 
    *isRunning = 0;
    pthread_mutex_unlock(mutex);
    //pthread_exit(NULL);
    return NULL;
}

int main(int argc, const char * argv[]) {
 
    Queue *mainQueue = createQueue();
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 
    pthread_t thread_id = 0;
    int isRunning = 1;
    void* args[] = {mainQueue, &isRunning, &mutex};
    pthread_create(&thread_id, NULL, thread_func, args);
 
    while(1)
    {
        pthread_mutex_lock(&mutex);
    
        if (size(mainQueue) > 0)
        {
            void(*func)(void) = peek(mainQueue);
            dequeue(mainQueue);
            func();
        }
    
        int running = isRunning;
        pthread_mutex_unlock(&mutex);
    
        if (!running)
        {
            break;
        }
    }
 
    pthread_join(thread_id, NULL);
    destroyQueue(&mainQueue);

    return 0;
}

Damn dude you really did this guy's work, with an explanation and all. Respect. As a fellow concurrent programmer I approve.
 

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
  • No one is chatting at the moment.
    SylverReZ @ SylverReZ: @salazarcosplay, Good.