0

I'm new to pthread and multithreading, i have written a code like that.

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


void *nfc_read(void *arg)
{
    int fd = -1;   
    int ret;
    uint8_t read_data[24];

    while(1){
        ret = read_block(fd, 8, read_data);
        if(!ret){
            return (void)read_data;
        }
    }
}

int main(int argc, char *argv[])
{
    pthread_t my_thread;
    void  *returnValue;

    pthread_create(&my_thread, NULL, nfc_read, NULL);
    pthread_join(my_thread, &returnValue);

    printf("NFC card value is : %s \n", (char)returnValue);

    printf("other process");

  return 0;
}

Until the return value from nfc_read function, as I will have other code I need to run and I don't want to block in main. How can i do that?

  • 1
    You put `pthread_join` at the point where you positively need the function to have completed. – zneak Jun 21 '17 at 04:12
  • 2
    If you don't want to block then don't call `pthread_join`. If you need the result of the thread then you will need to block or poll at some point. It's not clear exactly what behaviour you want. – kaylum Jun 21 '17 at 04:13
  • Thanks for the answer, if i remove **thread_join** function how can i get return value? – Sandaruwan Fernando Jun 21 '17 at 04:18
  • 3
    Also note that you're returning a local array which you cannot use after the function has finished – Sami Kuhmonen Jun 21 '17 at 04:52
  • @SandaruwanFernando you need to have some kind of event handling mechanism in the main thread; so you'd notify it that the NFC read has now completed. – Antti Haapala Jun 21 '17 at 04:58
  • @SamiKuhmonen do you have any event handling mechanism example for beginners? – Sandaruwan Fernando Jun 21 '17 at 05:05
  • 1
    @Scheff even for a "simple" thing like this, `volatile` is not sufficient: the threads might run on different CPUs, therefore requiring a memory barrier. The simplest thing to use might be pthread mutexes and condition variables. –  Jun 21 '17 at 06:07
  • @Scheff but in the code snippet, I don't see the need for synchronization other than `pthread_join()` at all. Just fixing the error of returning a "local variable" should produce working code. Maybe there's another problem not shown in OP's code. –  Jun 21 '17 at 06:12
  • 3
    Calling `pthread_create` immediately followed by `pthread_join` is just a very inefficient way to call a function. – Art Jun 21 '17 at 07:15
  • @FelixPalmen Using `volatile` without atomic access might be not sufficient but with atomic access it should. Atomic access is probably much cheaper than a mutex. I found this (accepted) [answer for C++](https://stackoverflow.com/a/9201088/7478597) (beside of the fact that I already did it this way by myself). I _assume_ that `std::atomic` is based on the same techniques like the functions in [Atomic Library](http://en.cppreference.com/w/c/atomic). – Scheff's Cat Jun 21 '17 at 07:42
  • @Scheff: I was refering to the first part of your comment. Of course, an *atomic* type is sufficient. Whether this is the simplest thing is debatable. If it's really just used for a flag, of course it is more efficient than locking. –  Jun 21 '17 at 07:50
  • @FelixPalmen I agree and replace my first comment: The IMHO simplest kind of simple event handling between threads is using a variable (e.g. of type `int`) which is written in one thread and (cyclically) read in the other thread. Thereby, the write as well as read access have to be done using atomic operations (since C11 provided with [Atomic Library](http://en.cppreference.com/w/c/atomic). Otherwise, the synchronization between threads might not be sufficient (as any CPU caching or similar internal effects may prevent this). – Scheff's Cat Jun 21 '17 at 07:55
  • Is there any solution about that? – Sandaruwan Fernando Jun 21 '17 at 08:31
  • May be, I did understand your question wrong. If your intention is to have a "never-ending" main loop and a concurrently running "never-ending" read loop, then you should make familiar with [pthread_mutex](https://linux.die.net/man/3/pthread_mutex_init). A mutex can be used for mutual exclusion of concurrent data accesses. The actual overhanding of data can then be done using allocated buffers, lists, queues, and anything else what is usually available. However, every read/write access has to be guarded by a mutex lock to prevent that one thread disturbs the other in the _critical section_. – Scheff's Cat Jun 21 '17 at 09:50
  • If I did understand your question wrong this might be caused by your misleading code sample. (I mean this as statement - not as offense.) May be, it's worth to edit it and expose the main loop somehow (even if it contains comments only). And, of course, `pthread_join()` is the function where two threads become one (the one which is calling it and the other for which the ID is provided). So, it has to be called after leaving the main loop. – Scheff's Cat Jun 21 '17 at 09:57
  • the posted code is missing a necessary statement: `#include ` – user3629249 Jun 21 '17 at 20:31
  • in the function: `main()`, the parameters are not being used. so should use the signature: `int main( void )` – user3629249 Jun 21 '17 at 20:32
  • before calling `read_block()` must have opened the input file, typically by `fopen()` or `open()` And there is no function: `read_block()` perhaps you meant `read()` – user3629249 Jun 21 '17 at 20:35
  • the posted code contains some 'magic' numbers. 'magic' numbers are numbers with no basis. I.E. 8, 24 'magic' numbers make the code much more difficult to understand, debug, etc. Suggest using a `enum` statement or `#define` statements to give those 'magic' numbers meaningful names, then use those meaningful names throughout the code. – user3629249 Jun 21 '17 at 20:37
  • a thread cannot return a (useful) pointer to a character array. However, it can return a pointer to a 'status'. The return type of a thread function is always `void*` so the function: `nfc_read()` should be returning a `void*` not a `void` – user3629249 Jun 21 '17 at 20:44
  • the posted code is missing the statement: `#include ` – user3629249 Jun 21 '17 at 20:49
  • the parameter to `nfc_read()` is not used, do the first line in the body of that function should be: `(void)arg;` – user3629249 Jun 21 '17 at 20:51
  • regarding the statement: `printf("NFC card value is : %s \n", (char)returnValue);` The format specifier: `%s` is expecting a pointer to a NUL terminated array of char (char*). Not (char) – user3629249 Jun 21 '17 at 20:53
  • what is this statement supposed to mean: `printf("other process");`? – user3629249 Jun 21 '17 at 20:54
  • the thread function: `nfc_read()` is returning the address of a local variable. That variable will be 'out of scope' when the function exits. So in `main()` that variable does not exist after the call to `pthread_join()` Trying to print the contents of that local variable, in `main()`, is undefined behavior. Suggest creating `read_data[]` via a call to `malloc()` or declaring it in `main()` and passing its address as the parameter to the thread function – user3629249 Jun 21 '17 at 21:02

1 Answers1

0

This is a sample where a read thread runs concurrently to the "main" thread which is doing other work (in this case, printing dots and sleeping).

To keep things simple, a simulated the reading from an input device with copying a constant string character by character. I guess, this is reasonable as the synchronization of threads is focused.

For the synchronization of threads, I used atomic_bool with atomic_store() and atomic_load() which are provided by the Atomic Library (since C11).

My sample application test-concurrent-read.c:

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

/* sampe input */
const char sampleInput[]
  = "This is sample input which is consumed as if it was read from"
    " a (very slow) external device.";

atomic_bool readDone = ATOMIC_VAR_INIT(0);

void* threadRead(void *pArg)
{
  char **pPBuffer = (char**)pArg;
  size_t len = 0, size = 0;
  int c; const char *pRead;
  for (pRead = sampleInput; (c = *pRead++) > 0; sleep(1)) {
    if (len + 1 >= size) {
      if (!(*pPBuffer = realloc(*pPBuffer, (size + 64) * sizeof(char)))) {
        fprintf(stderr, "ERROR! Allocation failed!\n");
        break;
      }
      size += 64;
    }
    (*pPBuffer)[len++] = c; (*pPBuffer)[len] = '\0';
  }
  atomic_store(&readDone, 1);
  return NULL;
}

int main()
{
  /* start thread to read concurrently */
  printf("Starting thread...\n");
  pthread_t idThreadRead; /* thread ID for read thread */
  char *pBuffer = NULL; /* pointer to return buffer from thread */
  if (pthread_create(&idThreadRead, NULL, &threadRead, &pBuffer)) {
    fprintf(stderr, "ERROR: Failed to start read thread!\n");
    return -1;
  }
  /* start main loop */
  printf("Starting main loop...\n");
  do {
    putchar('.'); fflush(stdout);
    sleep(1);
  } while (!atomic_load(&readDone));
  putchar('\n');
  void *ret;
  pthread_join(idThreadRead, &ret);
  /* after sync */
  printf("\nReceived: '%s'\n", pBuffer ? pBuffer : "<NULL>");
  free(pBuffer);
  /* done */
  return 0;
}

Compiled and tested with gcc in cygwin on Windows 10 (64 bit):

$ gcc -std=c11 -pthread -o test-concurrent-read test-concurrent-read.c

$ ./test-concurrent-read
Starting thread...
Starting main loop...
.............................................................................................

Received: 'This is sample input which is consumed as if it was read from a (very slow) external device.'

$ 

I guess, it is worth to mention why there is no mutex guarding for pBuffer which is used in main() as well as in threadRead().

  1. pBuffer is initialized in main() before pthread_create() is called.

  2. While thread_read() is running, pBuffer is used by it exclusively (via its passed address in pPBuffer).

  3. It is accessed in main() again but not before pthread_join() which grants that threadRead() has ended.

I tried to find a reference by google to confirm that this procedure is well-defined and reasonable. The best, I could find was SO: pthread_create(3) and memory synchronization guarantee in SMP architectures which cites The Open Group Base Specifications Issue 7 - 4.12 Memory Synchronization.

Scheff's Cat
  • 16,517
  • 5
  • 25
  • 45