Hello,
I'd like to write some reusabel code module to handle e.g. UART ports. Therefore I'd like to create a variable number (based on some kind of define...) of threads dealing with UART port 1,2,3 ... since it is possible to declare CMSIS-RTOS threads with a variable number of instances it could look like this:
#define UART_PORT_NUM 3 osThreadDef(uart_thread, osPriorityNormal, UART_PORT_NUM, 0)
my problem: If I need any kind of inter-thread communication (ITC) object, like mutexes or mail queues, for each of this threads I don't know how to declare them, since osMailQDef, osMutexDef and so on, are not designed with a "variable" number of instances...
Any suggestions?
For example: If I'd like to create some interface like
uart_write(uint8_t id, /*< UART channel e.g. 1, 2, 3 */ uint32_t len, /*< number of bytes data is pointing to */ void *data); /*< data to send */
I could use internally a CMSIS-RTOS memory pool to get pseudo dynamic <len> bytes to store the content of <data>. After that I send a signal/message to the thread handling UART<id> to write out this data (that could lead to multiple interrupt acknowledged UART periphery calls...) In this case all threads are equal and designed in a generic way, but I need multiple memory pools to deal with different priorities, otherwise e.g. one thread handling some kind of debug messages could occupy all memory and block another UART thread used for important process data...
Anyway, this is actually meant to be just an example. It doesn't matter if this is the best way handling UART interfaces.
The central question is: How can I create multiple instances of ITC objects?
But you would normally not use any thread at all to write out already formatted data. You would just use a queue of some kind, and have the interrupt pick up more data as the UART requests it.
Threads are best suited for either producing data that can be enqueued for transmission, or for picking up received data and processing it.
So you would normally have one thread that picks up received NMEA data, and decodes it so your program knows if there is a valid position and what that position is.
And you might have a different thread interfacing with a modem, inserting AT commands or data to the send queue and picking up responses from the receive queue.
And a third thread that decode monitor commands from a third UART and produce responses.
And maybe a third thread that might pick up received RFID strings, and validate them and store in some database.
So four almost identical interrupt handlers that only differs in what queue to pick up data from and what queue to post data to.
While four completely different threads with the actual business logic for the data expected to be sent and/or received for that specific UART.
So why would you want to insert a generic thread in the middle, that doesn't know anything about data protocols but just moves data to/from the different UART? Does interrupt handlers do this too well so you feel a need to complicate the design, while consuming extra resources?
As I said, it really doesn't matter if this example is the best approach handling UART ports.
I am still struggling with my actual question:
Has anyone a suitable approach to implement a mechanism to create multiple instances of an ITC object?
btw.: I saw a thread asking a similar question (about ITC objects in C++ classes...), unfortunately I can't find it anymore. There were no answers...
Maybe there shouldn't be an answer because there isn't any real design that needs it?
A function that knows what it is doing would also be expected to know where to pick up data or where to forward data. So the function would be expected to know what mutex to use.
If I create multiple copies/instances of such a function I have to make multiple copies/instances of its data input and output interfaces as well. If all of them have to share the same input or output source/sink, the priority concept is useless.
So why should I have the possibility to create multiple thread instances if I have no possibility to create separate communication channels?
Could you give me another practical example of using the feature of creating multiple instances of a thread?
And by the way: "One of the interesting possibilities of an RTOS is that you can create multiple running instances of the same base thread code. So, for example, you could write a thread to control a UART and then create two running instances of the same thread code. Here each instance of the UART code could manage a different UART." Quote Trevor Martin, "The Designer's Guide to the Cortex-M Processor Family", Capter 6, page 178
If you make a terminal server, for example handling a number of serially connected RFID readers, then you have a situation where each reader - i.e. each UART - produces identical data.
Then it would be meaningful to use identical code to process the actual data. But each thread would need to know exactly what interface it is associated with, so it would need to know exactly what mutex to use or what queue to listen to. So the threads wouldn't be anonymous and there would still not really be a need for a pool of mutex objects or queue objects.
And in the above case, each RFID reader would also be expected to have the same priority. So no need to control that per thread or per port or per queue.
In the other diretion, you can find many examples where there is a need for a pool of anonymous threads. Each taking input from the same queue. Each producing an output to place into another queue. Each with the same priority. The code implemented as a thread pool just because the input data requires some processing that involves a delay (possibly slow hardware like having to wait for a file operation) or because the processor has multiple cores. So multiple, symmetrical threads, are used to get higher concurrency.
But back to you - can you show any single situation where there would be a need for multiple, anonymous, mutex objects? Or multiple, anonymous, queues?
Start looking for this functionality first after you have a design that would actually work better with this type of functionality - I have never seen such a concept really needed. The closest thing would be a load balancer taking input requests and forwarding them to multiple stupid work units that aren't able to pickup requests from a single queue but has to be tricked into thinking they are alone. But such a situation still really don't need a pool of anonymous queues, since the balancer would like to actually look at queue lengths or response times to figure out which of the output queues that is best suited to get more work.
Symmetrical thread pools are a very common design concept, as an alternative to multiplexed code which requires less resources but instead adds more complexity. But symmetrical means same priority and same input/output.
Ok, last try:
Think of a device driver, e.g. to control a pump connected over a fieldbus. Depending on the complexity of such a driver module, it could even consist of multiple threads. You don't know in which project this driver will be included, but there could be multiple pumps, with different priorities (e.g. because the application knows that one pump has to be adjusted more frequently). Each driver instance would need its own message/mail queues to get input data from the application (for example a superordinate synchronization thread) and to send its output back. And as I said: If this module realy consists of multiple threads, it could be possible that it needs internally hidden, anonymous ITC objects.
About your own example: if you have such a terminal server and you know that there is one client talking a lot more than the others, it could be usefull to increase its priority
Perhaps we are talking past each other, but when I ask "I have a problem xy, any suggestions how to fix it?", aswers like "I can't imagine why you need this" are not really helpfull.
If I have such a terminal server, I wouldn't want it to increase the priority, since I would want each connection to be fair.
If your advanced pump driver needs multiple threads, then it is a very, very low probability that you would talk about identical threads. So you would want to set up multiple, specific, threads. And you each thread would want to know what specific input/output/synchronization objects to use.
So I would normally do:
create_thread(id_pump1,pump1_addr,pump1_event,pump1_prio,pump1_listener); create_thread(id_pump2,pump2_addr,pump2_event,pump2_prio,pump2_listener); ...
and would not have a need for multiple, identical, threads operating in an anonymous way on some anonymous pool of other ITC objects.
If a user wants to increase the speed of pump 5, the user needs to be able to address pump 5. So the user needs to have a specific pump 5 control object.
Same if the user wants to get an alarm because pump 2 fails to keep the intended rpm - then the user needs the thread operating pump 2 to know exactly where to send the reports.
That is why you would normally not see a need for an anonymous group of mutex objects. Or of event objects. Or of mail boxes. But while there is regularly a need for an anonymous group of threads.
You are constantly trying to invent some random system that would need a mutex pool. But you aren't managing to move from an invented system to instead describe a real, existing, usage case. While it's trivial to list thousands upon thousands of existing implementations needing a thread pool.
And look back one more time on osThreadDef().
It takes the parameters:
name name of the thread function. priority initial priority of the thread function. instances number of possible thread instances. stacksz stack size (in bytes) requirements for the thread function.
So you specify one (1) priority. But you can specify multiple instances.
But that terminal server solution can then perform multiple osThreadCreate() in which the last parameter allows each thread to know which UART it is responsible for. And that parameter can be a pointer to a struct that can look like:
struct term_port { uint32_t addr; // Base address of UART. osMessageQId send_queue_id; // Where to pick up outgoing messages. osMessageQId recv_queue_id; // Where to send received data. ... };
In the end, the rest of the world needs to know where to send data. And where to pick up data. And how to flag events. So the rest of the program needs well defined objects to operate on. So what reason would there be to have a osMutexDef() that takes a parameter "instances"? I either wants to send my output to a specific printer ("office floor 2") or I want to send it to a generic pool and have the first free printer pick up the data. So either I want each printer to have a named queue. Or I want all printers to look in a common queue and the first to grab the data wins.
With the common queue, I expect the printers to be supplying the same service. With the separate queues, I would expect each printer to be able to have different capabilities so I may select a printer that has duplex support. Or photo colors.
In the end, I have to repeat the same thing I said in the previous post. Don't ask for anonymous mutex pools, anonymous queue pools, ... until you actually have managed to find a problem that would be able to take good advantage of such functionality.
The world have had pthreads for a great many years, without anyone actually getting stuck because of such needs.
When you make posts to this thread, you aren't making the posts to an arbitrary pool of queues or trying to take one mutex from an arbitrary pool of mutexes. You post an addresses message reaching a specific queue affecting a specific mutex. The server may be redundant or load-sharing, in which case there is a pool of symmetrical threads listening for incomming data from that queue.
So in the end, the bulk of all functionality around you just happens to be designed around the two basic variants: - multiple workers load-sharing tasks received from a single boss. - multiple companies with individual email addresses or phone numbers.
Spend some time with computer-science books and you'll see lots of generic examples of producer-consumer systems, or publisher-subscriber systems. And the thread implementations are designed around these two concepts.
The only companies who likes "random mailboxes" are the spam companies. But the normal need is to have either an addressed push or an addressed pull.
Wow, you sir, are a huge JACKASS!
And you are so eloquent.