We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
I'm trying to use multiple maibox for communication between a parent task to other tasks daughters. But my parent task can only communicate with a single child task, after which the system seems to stop.
/* Struct messages */ struct mbx_messages { u8 cmd, ok; u8 pack[10]; }; /* Mailbox declarations */ os_mbx_declare(mbx_task_master, 1); os_mbx_declare(mbx_task_slave_1, 1); os_mbx_declare(mbx_task_slave_2, 1); /* Memory pool */ _declare_box(mbx_pool, sizeof(struct mbx_messages), 6); /* Master task */ __task void master_task(void) { struct mbx_messages *snd_msg, *rcv_msg; os_dly_wait(4); while(1) { os_dly_wait(4); if(uart_2_read_byte() == 'S') { if(os_mbx_check(&mbx_qtotalizer_sweepfp) != 0) { snd_msg = _alloc_box(mbx_pool); snd_msg->cmd = 1; os_mbx_send(&mbx_task_slave_1, snd_msg, 10); if(os_mbx_wait(&mbx_task_master, (void *)&rcv_msg, 10) != OS_R_TMO) { if(rcv_msg->cmd == 2) { snd_msg = _alloc_box(mbx_pool); snd_msg->cmd = 3; os_mbx_send(&mbx_task_slave_2, snd_msg, 10); if(os_mbx_wait(&mbx_task_master, (void *)&rcv_msg, 10) != OS_R_TMO) { if(rcv_msg->cmd == 4) { uart_2_write_str("cmd = 4\n"); } } } else { uart_2_write_str("cmd != 2\n"); } } _free_box(pool_sweepfp, rcv_msg); } } } } /* Slave 1 task */ __task void slave_1_task(void) { struct mbx_messages *snd_msg, *rcv_msg; os_dly_wait(4); while(1) { if(os_mbx_wait(&mbx_task_slave_1, (void *)&rcv_msg, 10) != OS_R_TMO) { snd_msg = _alloc_box(mbx_pool); if(rcv_msg->cmd == 1) snd_msg->cmd = 2; else uart_2_write_str("cmd != 1\n"); os_mbx_send(&mbx_task_master, snd_msg, 10); } _free_box(pool_sweepfp, rcv_msg); } } /* Slave 2 task */ __task void slave_2_task(void) { struct mbx_messages *snd_msg, *rcv_msg; os_dly_wait(4); while(1) { if(os_mbx_wait(&mbx_task_slave_2, (void *)&rcv_msg, 10) != OS_R_TMO) { snd_msg = _alloc_box(mbx_pool); if(rcv_msg->cmd == 3) snd_msg->cmd = 4; else uart_2_write_str("cmd != 4\n"); os_mbx_send(&mbx_task_master, snd_msg, 10); } _free_box(pool_sweepfp, rcv_msg); } } /* Boot task */ __task void boot_task(void) { uart_2_open(9600); _init_box(mbx_pool, sizeof(mbx_pool), sizeof(struct mbx_messages)); os_mbx_init(&mbx_task_master, sizeof(mbx_task_master)); os_mbx_init(&mbx_task_slave_1, sizeof(mbx_task_slave_1)); os_mbx_init(&mbx_task_slave_2, sizeof(mbx_task_slave_2)); os_tsk_create(master_task, 11); os_tsk_create(slave_1_task, 12); os_tsk_create(slave_2_task, 13); } /* Main */ int main(void) { os_sys_init(boot_task); }
That a call will be _free_box is not clearing the message sent recently? Message allocated by _alloc_box is automatically removed by os_mbx_wait?
You can send information using many mailboxes. But be very careful about a main thread that only waits and listens for a single mailbox queue. If you don't just make an instant poll there, but allows the thread to wait actual time, then an empty mailbox means that your thread will stay and wait there - and during that time will not check if anything happens on another mailbox.
When multithreaded applications for some reason are non-responsive, you - as developer - must make some form of flowchart where you analyze all points all the different threads can stop and wait for events, or waits for resource locks. And how these stops interacts with other threads.
If you feel it's too much work setting up a description of such inter-thread timing relations, then you have to consider not using threads in the first place. Threads can be fun, exciting, and a nice way to structure a program. But incorrectly implemented, they are good sources for deadlocks or concurrent updates (and hence destruction) of shared data. So threads must be used with discipline.
Ok, thanks for the reply. I'll explain my application (on the concept that tasks should be small and well defined, with specific functions):
com_dev_status_task - Task to monitor device status. This task will trigger other tasks. com_dev_block_task - Task responsible for locking the device. com_dev_get_info - Task that will request information from the device. com_dev_reset_task - Task to restart the device.
Thus, the task status is sending messages to other tasks, depending on what should be done with the device. These messages between tasks is basically it
struct com_dev_internal_msg { u8 cmd, id; u8 pack[10]; u8 ok; };
So, as I have a "master task" that triggers messages to other tasks, must esssa "master task" know when "slave tasks" executed the request. Such confirmation would also be a Mailbox. In this case, I will always have only one "slave task" running, others would be waiting for requests from the "master task".
I had thought to use events, but I need to send various information tasks, I opted for the mailbox. There is another way to structure this application? Or should a single task that will process all commands device?
If the master task only start one slave task at a time, and don't start a new task before the previous task is done, then you don't need multiple child tasks. It's enough with a single "worker" tasks that waits for a job to be handed out. The worker task checks what to do, performs the task and then report back before starting to wait for next thing to do.
The main reason for a program to consist of 4 tasks is that the individual tasks are likely to need to run concurrently with each other. Things that can be handled in sequence shouldn't be splitted into separate tasks unless you work on a hyperthreaded or multicore processor and wants to process multiple, non-related work blocks, concurrently.
So an image-processing program may have multiple tasks to get a quad-core processor to process a large 20Mpix image four times faster - or to process four images concurrently.
But an image-processing program running on a single-threaded processor would add complexity while slowing down the program, by having multiple threads to try to concurrently work with same image or concurrently work on multiple images.
In the end, you should make some form of sequence diagram where you show what things that needs to be performed concurrently by having multiple threads time-slicing through the work or where a higher-priority thread must be able to interrupt and perform a more important task before releasing the processor to lower-priority threads.
And that sequence diagram should also concern itself with how a higher-prio thread can lock up a lower-prio thread. Or how a lower-prio thread taking a resource lock can lock up a higher prio thread.
In the end - programs should be written as well-defined modules. So functionality is broken down into small and easy-to-understand functions. But these functions shouldn't be run by individual threads unless there is needs for concurrency. Or the sequence diagram becomes too complex if the work isn't split into multiple threads interacting using simple, and easy to overview, signaling, messaging, ... mechanisms.
Ok, as my processor is LPC2478 and the best option is to do a single task with sequential requests. Other tasks of my application would be:
display_update_task - Driven by update event or os_dly_wait log_save_task - Driven by mailbox for burning logs on the memory card
Thanks for the help.
That looks like reasonable tasks - thinks that is valuable to do concurrently.