7

This question is not related to something that i want to do at the moment, but something i wondered while reading the (GNU/Linux/ISO-C) Documentation for sprintf() printf() and setlocale().

The hypothetical problem:

Imagine a multithreaded application which uses printf/scanf family for user faced text output on one thread and printf/scanf for file or even network I/O on another thread. Now imagine a scenario where that application needs to use different encodings/locales for the different kinds of I/O. The way to set the locale/encoding is to use setlocale() which is explicitly marked as "MT-Unsafe" int the Linux Programmer's Manual.

ISO/IEC 9899:2018 has the following to say at 7.11.1.1

A call to the setlocale function may introduce a data race with other calls to the setlocale function or with calls to functions that are affected by the current locale. The implementation shall behave as if no library function calls the setlocale function.

As far as i see it, this leaves standard C with no standard way to handle the situation described prior. (that does not involve needless synchronization between threads that otherwise don't neccesairily interfere)

Note that POSIX specifies the uselocale() function which is specifically made for this. But this is no solution for i.e. embedded or tuly multi-platform code.

The question(s):

  • is there a portable, not cumbersome way to handle these kinds of situations without resorting to custom libaries
  • if not so: what might be the reason to ommit such capability from the standard

TL;DR: How to handle multithreaded encoding (outside POSIX)?

frhun
  • 160
  • 6

2 Answers2

2

You cannot safely use setlocale at all in a program once it becomes multithreaded. If you need multiple locales, you need the newlocale/uselocale API for thread-local or first-class locale objects, not setlocale.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
0

Maybe by using more or less standard mutexes? E.g. as explained at How to Use C Mutex Lock Examples for Linux Thread Synchronization:

Introduction

We employ pthread.h. The thread management would be then:

...
pthread_t tid[2];
pthread_create(&(tid[i]), NULL, &doSomeThing, NULL);
pthread_join(tid[0], NULL);
pthread_join(tid[1], NULL);
...

A mutex is initialized and then a lock is achieved by calling the following two functions:

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_lock(pthread_mutex_t *mutex);

The mutex can be unlocked and destroyed by calling following functions:

int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);

Practical Example

Lets see a piece of code where mutexes are used for thread synchronization:

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

pthread_t tid[2];
int counter;
pthread_mutex_t lock;

void* doSomeThing(void *arg)
{
    pthread_mutex_lock(&lock);

    unsigned long i = 0;
    counter += 1;
    printf("\n Job %d started\n", counter);

    for(i=0; i<(0xFFFFFFFF);i++);

    printf("\n Job %d finished\n", counter);

    pthread_mutex_unlock(&lock);

    return NULL;
}

int main(void)
{
    int i = 0;
    int err;

    if (pthread_mutex_init(&lock, NULL) != 0)
    {
        printf("\n mutex init failed\n");
        return 1;
    }

    while(i < 2)
    {
        err = pthread_create(&(tid[i]), NULL, &doSomeThing, NULL);
        if (err != 0)
            printf("\ncan't create thread :[%s]", strerror(err));
        i++;
    }

    pthread_join(tid[0], NULL);
    pthread_join(tid[1], NULL);
    pthread_mutex_destroy(&lock);

    return 0;
}
Dr. Andrey Belkin
  • 795
  • 1
  • 9
  • 28