0

I'm dealing with pipe communications between threads in C programming. I have 2 threads:

-thread 1 just manage some events,

-thread 2 communicates with a serial port;

Thread 1 and 2 communicates with a pipe.

The "events manager" thread if there are some conditions should send a string to the "serial manager" with e.g. pipe[1], which is polling out from serial port and from pipe[0]. Then if there's a string from pipe[0] it should do his work.

The problem is that thread 1 writes faster than threads 2 reads. So my question is: how do I have to properly read from pipe[0]? How do I have a queue? Because if I use read simply in blocking way just typing in thread 2:

read(pipe[0], string, sizeof(string)-1)

thread 2 reads all the thread 1 overload messages;

The only solution that I found is to create another pipe that blocks thread 1 (because thread 1 starts to read after writing, read is in blocking way), so thread 1 waits until thread 2 has done is work (this is useful 'cause I can get response from thread2), but my question is: is this the correct way? My convinction is that I'm missing something about read function.

Syrniellow
  • 3
  • 1
  • 4
  • Is there a good reason you're using a pipe to communicate between two threads? That seems like it just creates problems and has no benefits. – David Schwartz Jan 18 '18 at 00:23
  • You can use message queues instead of pipes. They are made for beeing queues! Check out this question: https://stackoverflow.com/questions/3056307/how-do-i-use-mqueue-in-a-c-program-on-a-linux-based-system – Piotr Styczyński Jan 18 '18 at 00:37
  • And if you are using threads then you can use other means of synchronization like C mutex? Pipes are meant to be used mostly with processes communication not really threads. – Piotr Styczyński Jan 18 '18 at 00:39
  • threads are running on the same "process context" and do **not** need pipes for communication. You can use thread synchronization in this case, like conditional variables or mutexes. Your question is more related to the parallel processes. In this case you can use pipes, fifos, shared memory with semaphores or other process synchronization techniques. – Serge Jan 18 '18 at 03:00
  • the init purpose of two threads was to write both to serial, but this created a lot of conflicts.So I've used binary semaphore (mutex) and shared storage, that works very fine but code was not 100% strong. Then I realized that only one thread should write/read serial, but I need another thread which codificates all the stuff to do and syntetize it into a string. I think pipe is a good solution, I can choose between 2 behaviors: - blocking way: thread 1 is blocked until thread2 say it's ok (original purpose) -non-blocking: they're quite indipendent, thread 1 loads buffer up to 65Kbytes ;) – Syrniellow Jan 18 '18 at 04:05

2 Answers2

1

"[I]s this the correct way [to read variable-length asynchronous messages over a FIFO, processing them one at a time]?"

No, you do not need to synchronize a single producer sending variable-length messages over a FIFO to a single consumer to process the messages one at a time.

As you documented in your own answer, you could add a record terminator to the messages. You could implement a simple protocol that describes the length of each message (c.f. netstrings).

There's a lot of prior practice you can borrow here. For example, you don't need to read record-terminated messages one character at a time, but could locally buffer partial messages — think of what stdio does to turn bytestreams into lines. The constraint that there's only one consumer makes some things easy.

"[I]s this the correct way [to send variable-length asynchronous messages between threads]?"

It's serviceable but perhaps not ideal.

A message-oriented, queuing channel might be better suited here: message queues or a datagram socket pair.

pilcrow
  • 56,591
  • 13
  • 94
  • 135
0

Thank you all for the answers. I've found the solution, don't know if it is stylistically correct but it works very well, using read function in non-blocking way. So just have to configure the pipe in the main:

fcntl(pipe_fd[0], F_SETFL, O_NONBLOCK);

Then just assure to write from thread1 the string plus the '\0' char:

write (pipe_fd[1], string, sizeof(string)+1);

And at last the read in the thread2 should be like this one

int n_bytes, offset;
int pres;
char ch[2];
string[256]; 

pres = poll (....);

if (pres > 0){
    ...
    ...
    /* if it's the pipe_fd[0] ...*/

    offset = 0;
    do{
       n_bytes = read(pipe_fd[0], &ch, 1);
       string[offset] = ch[0];
       offset += n;
    }while(n>0 && ch[0]!='\0' && offset < sizeof(string))

    work_with_message(string)....

that's it, tell me what you think ;-)

Syrniellow
  • 3
  • 1
  • 4