Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sal/osl/unx/thread.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <sal/config.h>
21
22
#include <cassert>
23
#include <functional>
24
#include <mutex>
25
#include <unistd.h>
26
27
#include "system.hxx"
28
#include "unixerrnostring.hxx"
29
#include <thread_internal.hxx>
30
31
#include <string.h>
32
#if defined(OPENBSD)
33
#include <sched.h>
34
#endif
35
#ifdef __FreeBSD__
36
#if __FreeBSD_version <= 1201517
37
#include <pthread_np.h>
38
#define pthread_setname_np pthread_set_name_np
39
#endif
40
#endif
41
#include <config_options.h>
42
#include <o3tl/safeint.hxx>
43
#include <osl/thread.h>
44
#include <osl/nlsupport.h>
45
#include <rtl/textenc.h>
46
#include <sal/log.hxx>
47
#include <sal/macros.h>
48
#ifdef ANDROID
49
#include <jni.h>
50
#include <android/log.h>
51
#include <osl/detail/android-bootstrap.h>
52
#endif
53
#if defined __EMSCRIPTEN__
54
#include <emscripten/console.h>
55
#endif
56
57
#if defined LINUX && ! defined __FreeBSD_kernel__
58
#include <sys/syscall.h>
59
#endif
60
61
/****************************************************************************
62
 * @@@ TODO @@@
63
 *
64
 * (1) 'osl_thread_priority_init_Impl()'
65
 *     - insane assumption that initializing caller is main thread
66
 *     - use _POSIX_THREAD_PRIORITY_SCHEDULING, not NO_PTHREAD_PRIORITY (?)
67
 *     - POSIX doesn't require defined prio's for SCHED_OTHER (!)
68
 *     - use SCHED_RR instead of SCHED_OTHER for defined behaviour (?)
69
 * (2) 'oslThreadIdentifier' and '{insert|remove|lookup}ThreadId()'
70
 *     - cannot reliably be applied to 'alien' threads;
71
 *     - memory leak for 'alien' thread 'HashEntry's;
72
 *     - use 'reinterpret_cast<unsigned long>(pthread_t)' as identifier
73
 *       instead (?)
74
 *     - if yes, change 'oslThreadIdentifier' to 'intptr_t' or similar
75
 * (3) 'oslSigAlarmHandler()' (#71232#)
76
 *     - [Under Solaris we get SIGALRM in e.g. pthread_join which terminates
77
 *       the process. So we initialize our signal handling module and do
78
 *       register a SIGALRM Handler which catches and ignores it]
79
 *     - should this still happen, 'signal.c' needs to be fixed instead.
80
 *
81
 ****************************************************************************/
82
83
34.8k
#define THREADIMPL_FLAGS_TERMINATE  0x00001
84
139k
#define THREADIMPL_FLAGS_STARTUP    0x00002
85
174k
#define THREADIMPL_FLAGS_SUSPENDED  0x00004
86
104k
#define THREADIMPL_FLAGS_ACTIVE     0x00008
87
206k
#define THREADIMPL_FLAGS_ATTACHED   0x00010
88
69.6k
#define THREADIMPL_FLAGS_DESTROYED  0x00020
89
90
namespace {
91
92
typedef struct osl_thread_impl_st
93
{
94
    pthread_t           m_hThread;
95
    oslThreadIdentifier m_Ident; /* @@@ see TODO @@@ */
96
    short               m_Flags;
97
    oslWorkerFunction   m_WorkerFunction;
98
    void*               m_pData;
99
    pthread_mutex_t     m_Lock;
100
    pthread_cond_t      m_Cond;
101
} Thread_Impl;
102
103
#if !defined NO_PTHREAD_PRIORITY
104
struct osl_thread_priority_st
105
{
106
    int m_Highest;
107
    int m_Above_Normal;
108
    int m_Normal;
109
    int m_Below_Normal;
110
    int m_Lowest;
111
};
112
#define OSL_THREAD_PRIORITY_INITIALIZER { 127, 96, 64, 32, 0 }
113
#endif
114
115
}
116
117
#if !defined NO_PTHREAD_PRIORITY
118
119
namespace {
120
121
struct osl_thread_global_st
122
{
123
    pthread_once_t                    m_once;
124
    struct osl_thread_priority_st     m_priority;
125
};
126
127
}
128
129
static struct osl_thread_global_st g_thread =
130
{
131
    PTHREAD_ONCE_INIT,
132
    OSL_THREAD_PRIORITY_INITIALIZER
133
};
134
135
#endif // !defined NO_PTHREAD_PRIORITY
136
137
static Thread_Impl* osl_thread_construct_Impl();
138
static void         osl_thread_destruct_Impl (Thread_Impl ** ppImpl);
139
140
static void* osl_thread_start_Impl (void * pData);
141
static void  osl_thread_cleanup_Impl (Thread_Impl * pImpl);
142
143
static oslThread osl_thread_create_Impl (
144
    oslWorkerFunction pWorker, void * pThreadData, short nFlags);
145
146
/* @@@ see TODO @@@ */
147
static oslThreadIdentifier insertThreadId (pthread_t hThread);
148
static oslThreadIdentifier lookupThreadId (pthread_t hThread);
149
static void                removeThreadId (pthread_t hThread);
150
151
Thread_Impl* osl_thread_construct_Impl()
152
34.8k
{
153
34.8k
    Thread_Impl* pImpl = new Thread_Impl;
154
34.8k
    memset (pImpl, 0, sizeof(Thread_Impl));
155
156
34.8k
    pthread_mutex_init (&(pImpl->m_Lock), PTHREAD_MUTEXATTR_DEFAULT);
157
34.8k
    pthread_cond_init  (&(pImpl->m_Cond), PTHREAD_CONDATTR_DEFAULT);
158
34.8k
    return pImpl;
159
34.8k
}
160
161
static void osl_thread_destruct_Impl (Thread_Impl ** ppImpl)
162
34.8k
{
163
34.8k
    assert(ppImpl);
164
34.8k
    if (*ppImpl)
165
34.8k
    {
166
34.8k
        pthread_cond_destroy  (&((*ppImpl)->m_Cond));
167
34.8k
        pthread_mutex_destroy (&((*ppImpl)->m_Lock));
168
169
34.8k
        delete *ppImpl;
170
34.8k
        (*ppImpl) = nullptr;
171
34.8k
    }
172
34.8k
}
173
174
static void osl_thread_cleanup_Impl (Thread_Impl * pImpl)
175
34.8k
{
176
34.8k
    pthread_t thread;
177
34.8k
    bool attached;
178
34.8k
    bool destroyed;
179
180
34.8k
    pthread_mutex_lock (&(pImpl->m_Lock));
181
182
34.8k
    thread = pImpl->m_hThread;
183
34.8k
    attached = (pImpl->m_Flags & THREADIMPL_FLAGS_ATTACHED) != 0;
184
34.8k
    destroyed = (pImpl->m_Flags & THREADIMPL_FLAGS_DESTROYED) != 0;
185
34.8k
    pImpl->m_Flags &= ~(THREADIMPL_FLAGS_ACTIVE | THREADIMPL_FLAGS_ATTACHED);
186
187
34.8k
    pthread_mutex_unlock (&(pImpl->m_Lock));
188
189
    /* release oslThreadIdentifier @@@ see TODO @@@ */
190
34.8k
    removeThreadId (thread);
191
192
34.8k
    if (attached)
193
27.4k
    {
194
27.4k
        pthread_detach (thread);
195
27.4k
    }
196
197
34.8k
    if (destroyed)
198
0
    {
199
0
        osl_thread_destruct_Impl (&pImpl);
200
0
    }
201
34.8k
}
202
203
static void* osl_thread_start_Impl (void* pData)
204
34.8k
{
205
34.8k
    bool terminate;
206
34.8k
    Thread_Impl* pImpl= static_cast<Thread_Impl*>(pData);
207
208
34.8k
    assert(pImpl);
209
210
34.8k
    pthread_mutex_lock (&(pImpl->m_Lock));
211
212
    /* request oslThreadIdentifier @@@ see TODO @@@ */
213
34.8k
    pImpl->m_Ident = insertThreadId (pImpl->m_hThread);
214
215
    /* signal change from STARTUP to ACTIVE state */
216
34.8k
    pImpl->m_Flags &= ~THREADIMPL_FLAGS_STARTUP;
217
34.8k
    pImpl->m_Flags |=  THREADIMPL_FLAGS_ACTIVE;
218
34.8k
    pthread_cond_signal (&(pImpl->m_Cond));
219
220
    /* Check if thread is started in SUSPENDED state */
221
69.6k
    while (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
222
34.8k
    {
223
        /* wait until SUSPENDED flag is cleared */
224
34.8k
        pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
225
34.8k
    }
226
227
    /* check for SUSPENDED to TERMINATE state change */
228
34.8k
    terminate = ((pImpl->m_Flags & THREADIMPL_FLAGS_TERMINATE) > 0);
229
230
34.8k
    pthread_mutex_unlock (&(pImpl->m_Lock));
231
232
34.8k
    if (!terminate)
233
34.8k
    {
234
#ifdef ANDROID
235
        JNIEnv* env = 0;
236
        int res = (*lo_get_javavm()).AttachCurrentThread(&env, NULL);
237
        __android_log_print(ANDROID_LOG_INFO, "LibreOffice", "New sal thread started and attached res=%d", res);
238
#endif
239
        /* call worker function */
240
34.8k
        pImpl->m_WorkerFunction(pImpl->m_pData);
241
242
#ifdef ANDROID
243
        res = (*lo_get_javavm()).DetachCurrentThread();
244
        __android_log_print(ANDROID_LOG_INFO, "LibreOffice", "Detached finished sal thread res=%d", res);
245
#endif
246
34.8k
    }
247
248
34.8k
    osl_thread_cleanup_Impl (pImpl);
249
34.8k
    return nullptr;
250
34.8k
}
251
252
static oslThread osl_thread_create_Impl (
253
    oslWorkerFunction pWorker,
254
    void*             pThreadData,
255
    short             nFlags)
256
34.8k
{
257
34.8k
    Thread_Impl* pImpl;
258
34.8k
#if defined OPENBSD || defined MACOSX || (defined LINUX && !ENABLE_RUNTIME_OPTIMIZATIONS)
259
34.8k
    pthread_attr_t attr;
260
34.8k
    size_t stacksize;
261
34.8k
#endif
262
34.8k
    int nRet=0;
263
264
34.8k
    pImpl = osl_thread_construct_Impl();
265
34.8k
    if (!pImpl)
266
0
        return nullptr; /* ENOMEM */
267
268
34.8k
    pImpl->m_WorkerFunction = pWorker;
269
34.8k
    pImpl->m_pData = pThreadData;
270
34.8k
    pImpl->m_Flags = nFlags | THREADIMPL_FLAGS_STARTUP;
271
272
34.8k
    pthread_mutex_lock (&(pImpl->m_Lock));
273
274
34.8k
#if defined OPENBSD || defined MACOSX || (defined LINUX && !ENABLE_RUNTIME_OPTIMIZATIONS)
275
34.8k
    if (pthread_attr_init(&attr) != 0)
276
0
        return nullptr;
277
278
#if defined OPENBSD
279
    stacksize = 262144;
280
#elif !ENABLE_RUNTIME_OPTIMIZATIONS
281
34.8k
    stacksize = 12 * 1024 * 1024; // 8MB is not enough for ASAN on x86-64
282
#else
283
    stacksize = 1 * 1024 * 1024; // macOS default for non-main threads (512kB) is not enough...
284
#endif
285
34.8k
    if (pthread_attr_setstacksize(&attr, stacksize) != 0) {
286
0
        pthread_attr_destroy(&attr);
287
0
        return nullptr;
288
0
    }
289
34.8k
#endif
290
291
34.8k
    if ((nRet = pthread_create (
292
34.8k
        &(pImpl->m_hThread),
293
34.8k
#if defined OPENBSD || defined MACOSX || (defined LINUX && !ENABLE_RUNTIME_OPTIMIZATIONS)
294
34.8k
        &attr,
295
#else
296
        PTHREAD_ATTR_DEFAULT,
297
#endif
298
34.8k
        osl_thread_start_Impl,
299
34.8k
        static_cast<void*>(pImpl))) != 0)
300
0
    {
301
0
        SAL_WARN(
302
0
            "sal.osl",
303
0
            "pthread_create failed: " << UnixErrnoString(nRet));
304
305
0
        pthread_mutex_unlock (&(pImpl->m_Lock));
306
0
        osl_thread_destruct_Impl (&pImpl);
307
308
0
        return nullptr;
309
0
    }
310
311
34.8k
#if defined OPENBSD || defined MACOSX || (defined LINUX && !ENABLE_RUNTIME_OPTIMIZATIONS)
312
34.8k
    pthread_attr_destroy(&attr);
313
34.8k
#endif
314
315
    /* wait for change from STARTUP to ACTIVE state */
316
69.6k
    while (pImpl->m_Flags & THREADIMPL_FLAGS_STARTUP)
317
34.8k
    {
318
        /* wait until STARTUP flag is cleared */
319
34.8k
        pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
320
34.8k
    }
321
322
34.8k
    pthread_mutex_unlock (&(pImpl->m_Lock));
323
324
34.8k
    return static_cast<oslThread>(pImpl);
325
34.8k
}
326
327
oslThread osl_createThread (
328
    oslWorkerFunction pWorker,
329
    void *            pThreadData)
330
6
{
331
6
    return osl_thread_create_Impl (
332
6
        pWorker,
333
6
        pThreadData,
334
6
        THREADIMPL_FLAGS_ATTACHED);
335
6
}
336
337
oslThread osl_createSuspendedThread (
338
    oslWorkerFunction pWorker,
339
    void *            pThreadData)
340
34.8k
{
341
34.8k
    return osl_thread_create_Impl (
342
34.8k
        pWorker,
343
34.8k
        pThreadData,
344
34.8k
        THREADIMPL_FLAGS_ATTACHED |
345
34.8k
        THREADIMPL_FLAGS_SUSPENDED );
346
34.8k
}
347
348
void SAL_CALL osl_destroyThread(oslThread Thread)
349
34.8k
{
350
34.8k
    if (Thread != nullptr) {
351
34.8k
        Thread_Impl * impl = static_cast<Thread_Impl *>(Thread);
352
34.8k
        bool active;
353
34.8k
        pthread_mutex_lock(&impl->m_Lock);
354
34.8k
        active = (impl->m_Flags & THREADIMPL_FLAGS_ACTIVE) != 0;
355
34.8k
        impl->m_Flags |= THREADIMPL_FLAGS_DESTROYED;
356
34.8k
        pthread_mutex_unlock(&impl->m_Lock);
357
34.8k
        if (!active) {
358
34.8k
            osl_thread_destruct_Impl(&impl);
359
34.8k
        }
360
34.8k
    }
361
34.8k
}
362
363
void SAL_CALL osl_resumeThread(oslThread Thread)
364
34.8k
{
365
34.8k
    Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
366
367
34.8k
    if (!pImpl)
368
0
    {
369
0
        SAL_WARN("sal.osl", "invalid osl_resumeThread(nullptr) call");
370
0
        return; /* EINVAL */
371
0
    }
372
373
34.8k
    pthread_mutex_lock (&(pImpl->m_Lock));
374
375
34.8k
    if (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
376
34.8k
    {
377
        /* clear SUSPENDED flag */
378
34.8k
        pImpl->m_Flags &= ~THREADIMPL_FLAGS_SUSPENDED;
379
34.8k
        pthread_cond_signal (&(pImpl->m_Cond));
380
34.8k
    }
381
382
34.8k
    pthread_mutex_unlock (&(pImpl->m_Lock));
383
34.8k
}
384
385
void SAL_CALL osl_suspendThread(oslThread Thread)
386
0
{
387
0
    Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
388
389
0
    if (!pImpl)
390
0
    {
391
0
        SAL_WARN("sal.osl", "invalid osl_suspendThread(nullptr) call");
392
0
        return; /* EINVAL */
393
0
    }
394
395
0
    pthread_mutex_lock (&(pImpl->m_Lock));
396
397
0
    pImpl->m_Flags |= THREADIMPL_FLAGS_SUSPENDED;
398
399
0
    if (pthread_equal (pthread_self(), pImpl->m_hThread))
400
0
    {
401
        /* self suspend */
402
0
        while (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
403
0
        {
404
            /* wait until SUSPENDED flag is cleared */
405
0
            pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
406
0
        }
407
0
    }
408
409
0
    pthread_mutex_unlock (&(pImpl->m_Lock));
410
0
}
411
412
sal_Bool SAL_CALL osl_isThreadRunning(const oslThread Thread)
413
0
{
414
0
    bool active;
415
0
    Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
416
417
0
    if (!pImpl)
418
0
        return false;
419
420
0
    pthread_mutex_lock (&(pImpl->m_Lock));
421
0
    active = ((pImpl->m_Flags & THREADIMPL_FLAGS_ACTIVE) > 0);
422
0
    pthread_mutex_unlock (&(pImpl->m_Lock));
423
424
0
    return active;
425
0
}
426
427
void SAL_CALL osl_joinWithThread(oslThread Thread)
428
51.1k
{
429
51.1k
    Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
430
431
51.1k
    if (!pImpl)
432
0
        return;
433
434
51.1k
    pthread_mutex_lock (&(pImpl->m_Lock));
435
436
51.1k
    pthread_t const thread = pImpl->m_hThread;
437
51.1k
    bool const attached = ((pImpl->m_Flags & THREADIMPL_FLAGS_ATTACHED) > 0);
438
439
    /* check this only if *this* thread is still attached - if it's not,
440
       then it could have terminated and another newly created thread could
441
       have recycled the same id as m_hThread! */
442
51.1k
    if (attached && pthread_equal(pthread_self(), pImpl->m_hThread))
443
0
    {
444
0
        assert(false); /* Win32 implementation would deadlock here! */
445
        /* self join */
446
0
        pthread_mutex_unlock (&(pImpl->m_Lock));
447
0
        return; /* EDEADLK */
448
0
    }
449
450
51.1k
    pImpl->m_Flags &= ~THREADIMPL_FLAGS_ATTACHED;
451
452
51.1k
    pthread_mutex_unlock (&(pImpl->m_Lock));
453
454
51.1k
    if (attached)
455
7.35k
    {
456
7.35k
        pthread_join (thread, nullptr);
457
7.35k
    }
458
51.1k
}
459
460
void SAL_CALL osl_terminateThread(oslThread Thread)
461
0
{
462
0
    Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
463
464
0
    if (!pImpl)
465
0
    {
466
0
        SAL_WARN("sal.osl", "invalid osl_terminateThread(nullptr) call");
467
0
        return; /* EINVAL */
468
0
    }
469
470
0
    pthread_mutex_lock (&(pImpl->m_Lock));
471
472
0
    if (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
473
0
    {
474
        /* clear SUSPENDED flag */
475
0
        pImpl->m_Flags &= ~THREADIMPL_FLAGS_SUSPENDED;
476
0
        pthread_cond_signal (&(pImpl->m_Cond));
477
0
    }
478
479
0
    pImpl->m_Flags |= THREADIMPL_FLAGS_TERMINATE;
480
481
0
    pthread_mutex_unlock (&(pImpl->m_Lock));
482
0
}
483
484
sal_Bool SAL_CALL osl_scheduleThread(oslThread Thread)
485
0
{
486
0
    bool terminate;
487
0
    Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
488
489
0
    if (!pImpl)
490
0
    {
491
0
        SAL_WARN("sal.osl", "invalid osl_scheduleThread(nullptr) call");
492
0
        return false; /* EINVAL */
493
0
    }
494
495
0
    if (!(pthread_equal (pthread_self(), pImpl->m_hThread)))
496
0
    {
497
0
        SAL_WARN("sal.osl", "invalid osl_scheduleThread(non-self) call");
498
0
        return false; /* EINVAL */
499
0
    }
500
501
0
    pthread_mutex_lock (&(pImpl->m_Lock));
502
503
0
    while (pImpl->m_Flags & THREADIMPL_FLAGS_SUSPENDED)
504
0
    {
505
        /* wait until SUSPENDED flag is cleared */
506
0
        pthread_cond_wait (&(pImpl->m_Cond), &(pImpl->m_Lock));
507
0
    }
508
509
0
    terminate = ((pImpl->m_Flags & THREADIMPL_FLAGS_TERMINATE) > 0);
510
511
0
    pthread_mutex_unlock(&(pImpl->m_Lock));
512
513
0
    return !terminate;
514
0
}
515
516
void SAL_CALL osl_waitThread(const TimeValue* pDelay)
517
0
{
518
0
    if (pDelay)
519
0
    {
520
0
        struct timespec delay;
521
522
0
        SET_TIMESPEC(delay, pDelay->Seconds, pDelay->Nanosec);
523
524
0
        SLEEP_TIMESPEC(delay);
525
0
    }
526
0
}
527
528
/** Yields thread
529
530
    @attention Note that POSIX scheduling @em really requires threads to call this
531
    function, since a thread only reschedules to other thread, when
532
    it blocks (sleep, blocking I/O) OR calls sched_yield().
533
*/
534
void SAL_CALL osl_yieldThread()
535
0
{
536
0
    sched_yield();
537
0
}
538
539
void SAL_CALL osl_setThreadName(char const * name)
540
34.8k
{
541
34.8k
    assert( name );
542
34.8k
#if defined LINUX && ! defined __FreeBSD_kernel__
543
34.8k
    const int LINUX_THREAD_NAME_MAXLEN = 15;
544
34.8k
    if ( strlen( name ) > LINUX_THREAD_NAME_MAXLEN )
545
34.8k
        SAL_INFO( "sal.osl", "osl_setThreadName truncated thread name to "
546
34.8k
                  << LINUX_THREAD_NAME_MAXLEN << " chars from name '"
547
34.8k
                  << name << "'" );
548
34.8k
    char shortname[ LINUX_THREAD_NAME_MAXLEN + 1 ];
549
34.8k
    shortname[ LINUX_THREAD_NAME_MAXLEN ] = '\0';
550
34.8k
    strncpy( shortname, name, LINUX_THREAD_NAME_MAXLEN );
551
34.8k
    int err = pthread_setname_np( pthread_self(), shortname );
552
34.8k
    if ( 0 != err )
553
34.8k
        SAL_WARN("sal.osl", "pthread_setname_np failed with errno " << err);
554
#elif defined __FreeBSD__
555
    pthread_setname_np( pthread_self(), name );
556
#elif defined MACOSX || defined IOS
557
    pthread_setname_np( name );
558
#elif defined __EMSCRIPTEN__
559
    emscripten_console_logf("Thread name: \"%s\"", name);
560
#else
561
    (void) name;
562
#endif
563
34.8k
}
564
565
/* osl_getThreadIdentifier @@@ see TODO @@@ */
566
567
namespace {
568
569
struct HashEntry
570
{
571
    pthread_t            Handle;
572
    oslThreadIdentifier  Ident;
573
    HashEntry *          Next;
574
};
575
576
}
577
578
static HashEntry* HashTable[31];
579
const int HashSize = SAL_N_ELEMENTS(HashTable);
580
581
static std::mutex HashLock;
582
583
#if ! ((defined LINUX && !defined __FreeBSD_kernel__) || defined MACOSX || defined IOS)
584
static oslThreadIdentifier LastIdent = 0;
585
#endif
586
587
namespace {
588
589
std::size_t HASHID(pthread_t x)
590
692M
{ return std::hash<pthread_t>()(x) % HashSize; }
591
592
}
593
594
static oslThreadIdentifier lookupThreadId (pthread_t hThread)
595
692M
{
596
692M
    HashEntry *pEntry;
597
598
692M
    std::unique_lock aGuard(HashLock);
599
600
692M
    pEntry = HashTable[HASHID(hThread)];
601
692M
    while (pEntry != nullptr)
602
692M
    {
603
692M
        if (pthread_equal(pEntry->Handle, hThread))
604
692M
        {
605
692M
            return pEntry->Ident;
606
692M
        }
607
426k
        pEntry = pEntry->Next;
608
426k
    }
609
610
17
    return 0;
611
692M
}
612
613
static oslThreadIdentifier insertThreadId (pthread_t hThread)
614
34.9k
{
615
34.9k
    HashEntry *pEntry, *pInsert = nullptr;
616
617
34.9k
    std::unique_lock aGuard(HashLock);
618
619
34.9k
    pEntry = HashTable[HASHID(hThread)];
620
621
39.5k
    while (pEntry != nullptr)
622
4.58k
    {
623
4.58k
        if (pthread_equal(pEntry->Handle, hThread))
624
0
            break;
625
626
4.58k
        pInsert = pEntry;
627
4.58k
        pEntry = pEntry->Next;
628
4.58k
    }
629
630
34.9k
    if (pEntry == nullptr)
631
34.9k
    {
632
34.9k
        pEntry = static_cast<HashEntry*>(calloc(1, sizeof(HashEntry)));
633
634
34.9k
        pEntry->Handle = hThread;
635
636
34.9k
#if defined LINUX && ! defined __FreeBSD_kernel__
637
34.9k
#if defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30))
638
        // gettid returns a pid_t, which POSIX defines to be a signed integer type; assume that all
639
        // valid pid_t values on Linux are positive (zero is filtered out in the generic code
640
        // below):
641
34.9k
        pid_t const tid = gettid();
642
34.9k
        assert(tid >= 0);
643
#else
644
        long const tid = syscall(SYS_gettid);
645
        if (tid < 0 || o3tl::make_unsigned(tid) > std::numeric_limits<sal_uInt32>::max()) {
646
            std::abort();
647
        }
648
#endif
649
34.9k
        pEntry->Ident = tid;
650
#elif defined MACOSX || defined IOS
651
        // currently the value of pthread_threadid_np is the same then
652
        // syscall(SYS_thread_selfid), which returns an int as the TID.
653
        // may change, as the syscall interface was deprecated.
654
        uint64_t mac_tid;
655
        pthread_threadid_np(nullptr, &mac_tid);
656
        if (mac_tid > SAL_MAX_UINT32)
657
            std::abort();
658
        pEntry->Ident = mac_tid;
659
#else
660
        ++LastIdent;
661
        if (0 == LastIdent)
662
            LastIdent = 1;
663
        pEntry->Ident = LastIdent;
664
#endif
665
34.9k
        if (0 == pEntry->Ident)
666
0
            std::abort();
667
668
34.9k
        if (pInsert)
669
4.30k
            pInsert->Next = pEntry;
670
30.6k
        else
671
30.6k
            HashTable[HASHID(hThread)] = pEntry;
672
34.9k
    }
673
674
34.9k
    return pEntry->Ident;
675
34.9k
}
676
677
static void removeThreadId (pthread_t hThread)
678
34.8k
{
679
34.8k
    HashEntry *pEntry, *pRemove = nullptr;
680
681
34.8k
    std::unique_lock aGuard(HashLock);
682
683
34.8k
    pEntry = HashTable[HASHID(hThread)];
684
38.7k
    while (pEntry != nullptr)
685
38.7k
    {
686
38.7k
        if (pthread_equal(pEntry->Handle, hThread))
687
34.8k
            break;
688
689
3.86k
        pRemove = pEntry;
690
3.86k
        pEntry = pEntry->Next;
691
3.86k
    }
692
693
34.8k
    if (pEntry != nullptr)
694
34.8k
    {
695
34.8k
        if (pRemove)
696
3.74k
            pRemove->Next = pEntry->Next;
697
31.0k
        else
698
31.0k
            HashTable[HASHID(hThread)] = pEntry->Next;
699
700
34.8k
        free(pEntry);
701
34.8k
    }
702
34.8k
}
703
704
oslThreadIdentifier SAL_CALL osl_getThreadIdentifier(oslThread Thread)
705
692M
{
706
692M
    Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
707
692M
    oslThreadIdentifier Ident;
708
709
692M
    if (pImpl)
710
0
        Ident = pImpl->m_Ident;
711
692M
    else
712
692M
    {
713
        /* current thread */
714
692M
        pthread_t current = pthread_self();
715
716
692M
        Ident = lookupThreadId (current);
717
692M
        if (Ident == 0)
718
            /* @@@ see TODO: alien pthread_self() @@@ */
719
108
            Ident = insertThreadId (current);
720
692M
    }
721
722
692M
    return Ident;
723
692M
}
724
725
#ifndef NO_PTHREAD_PRIORITY
726
/*****************************************************************************
727
    @@@ see TODO @@@
728
    osl_thread_priority_init_Impl
729
730
    set the base-priority of the main-thread to
731
    oslThreadPriorityNormal (64) since 0 (lowest) is
732
    the system default. This behaviour collides with
733
    our enum-priority definition (highest..normal..lowest).
734
    A  normaluser will expect the main-thread of an app.
735
    to have the "normal" priority.
736
737
*****************************************************************************/
738
static void osl_thread_priority_init_Impl()
739
{
740
    struct sched_param param;
741
    int policy=0;
742
    int nRet=0;
743
744
/* @@@ see TODO: calling thread may not be main thread @@@ */
745
746
    if ((nRet = pthread_getschedparam(pthread_self(), &policy, &param)) != 0)
747
    {
748
        SAL_WARN(
749
            "sal.osl",
750
            "pthread_getschedparam failed: " << UnixErrnoString(nRet));
751
        return;
752
    }
753
754
#if defined (__sun)
755
    if ( policy >= _SCHED_NEXT)
756
    {
757
        /* mfe: pthread_getschedparam on Solaris has a possible Bug */
758
        /*      one gets 959917873 as the policy                    */
759
        /*      so set the policy to a default one                  */
760
        policy=SCHED_OTHER;
761
    }
762
#endif /* __sun */
763
764
    if ((nRet = sched_get_priority_min(policy) ) != -1)
765
    {
766
        SAL_INFO(
767
            "sal.osl", "Min Prioriy for policy " << policy << " == " << nRet);
768
        g_thread.m_priority.m_Lowest=nRet;
769
    }
770
    else
771
    {
772
        int e = errno;
773
        SAL_WARN(
774
            "sal.osl",
775
            "sched_get_priority_min failed: " << UnixErrnoString(e));
776
    }
777
778
    if ((nRet = sched_get_priority_max(policy) ) != -1)
779
    {
780
        SAL_INFO(
781
            "sal.osl", "Max Prioriy for policy " << policy << " == " << nRet);
782
        g_thread.m_priority.m_Highest=nRet;
783
    }
784
    else
785
    {
786
        int e = errno;
787
        SAL_WARN(
788
            "sal.osl",
789
            "sched_get_priority_max failed: " << UnixErrnoString(e));
790
    }
791
792
    g_thread.m_priority.m_Normal =
793
        (g_thread.m_priority.m_Lowest + g_thread.m_priority.m_Highest) / 2;
794
    g_thread.m_priority.m_Below_Normal =
795
        (g_thread.m_priority.m_Lowest + g_thread.m_priority.m_Normal)  / 2;
796
    g_thread.m_priority.m_Above_Normal =
797
        (g_thread.m_priority.m_Normal + g_thread.m_priority.m_Highest) / 2;
798
799
/* @@@ set prio of calling (not main) thread (?) @@@ */
800
801
    param.sched_priority= g_thread.m_priority.m_Normal;
802
803
    if ((nRet = pthread_setschedparam(pthread_self(), policy, &param)) != 0)
804
    {
805
        SAL_WARN(
806
            "sal.osl",
807
            "pthread_setschedparam failed: " << UnixErrnoString(nRet));
808
        SAL_INFO(
809
            "sal.osl",
810
            "Thread ID " << pthread_self() << ", Policy " << policy
811
                << ", Priority " << param.sched_priority);
812
    }
813
814
}
815
#endif /* NO_PTHREAD_PRIORITY */
816
817
/**
818
    Impl-Notes: contrary to solaris-docu, which claims
819
    valid priority-levels from 0 .. INT_MAX, only the
820
    range 0..127 is accepted. (0 lowest, 127 highest)
821
*/
822
void SAL_CALL osl_setThreadPriority (
823
    oslThread         Thread,
824
    oslThreadPriority Priority)
825
0
{
826
#ifndef NO_PTHREAD_PRIORITY
827
828
    struct sched_param Param;
829
    int policy;
830
    int nRet;
831
832
#endif /* NO_PTHREAD_PRIORITY */
833
834
0
    Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
835
836
0
    if (!pImpl)
837
0
    {
838
0
        SAL_WARN("sal.osl", "invalid osl_setThreadPriority(nullptr, ...) call");
839
0
        return; /* EINVAL */
840
0
    }
841
842
0
#ifdef NO_PTHREAD_PRIORITY
843
0
    (void) Priority; /* unused */
844
#else /* NO_PTHREAD_PRIORITY */
845
846
    if (pthread_getschedparam(pImpl->m_hThread, &policy, &Param) != 0)
847
        return; /* ESRCH */
848
849
#if defined (__sun)
850
    if ( policy >= _SCHED_NEXT)
851
    {
852
        /* mfe: pthread_getschedparam on Solaris has a possible Bug */
853
        /*      one gets 959917873 as the policy                   */
854
        /*      so set the policy to a default one                 */
855
        policy=SCHED_OTHER;
856
    }
857
#endif /* __sun */
858
859
    pthread_once (&(g_thread.m_once), osl_thread_priority_init_Impl);
860
861
    switch(Priority)
862
    {
863
        case osl_Thread_PriorityHighest:
864
            Param.sched_priority= g_thread.m_priority.m_Highest;
865
            break;
866
867
        case osl_Thread_PriorityAboveNormal:
868
            Param.sched_priority= g_thread.m_priority.m_Above_Normal;
869
            break;
870
871
        case osl_Thread_PriorityNormal:
872
            Param.sched_priority= g_thread.m_priority.m_Normal;
873
            break;
874
875
        case osl_Thread_PriorityBelowNormal:
876
            Param.sched_priority= g_thread.m_priority.m_Below_Normal;
877
            break;
878
879
        case osl_Thread_PriorityLowest:
880
            Param.sched_priority= g_thread.m_priority.m_Lowest;
881
            break;
882
883
        case osl_Thread_PriorityUnknown:
884
            SAL_WARN(
885
                "sal.osl",
886
                "invalid osl_setThreadPriority(..., osl_Thread_PriorityUnknown)"
887
                    " call");
888
            return;
889
890
        default:
891
            SAL_WARN(
892
                "sal.osl",
893
                "invalid osl_setThreadPriority(..., " << Priority << ") call");
894
            return;
895
    }
896
897
    if ((nRet = pthread_setschedparam(pImpl->m_hThread, policy, &Param)) != 0)
898
    {
899
        SAL_WARN(
900
            "sal.osl",
901
            "pthread_setschedparam failed: " << UnixErrnoString(nRet));
902
    }
903
904
#endif /* NO_PTHREAD_PRIORITY */
905
0
}
906
907
oslThreadPriority SAL_CALL osl_getThreadPriority(const oslThread Thread)
908
0
{
909
#ifndef NO_PTHREAD_PRIORITY
910
911
    struct sched_param Param;
912
    int Policy;
913
914
#endif /* NO_PTHREAD_PRIORITY */
915
916
0
    oslThreadPriority Priority = osl_Thread_PriorityNormal;
917
0
    Thread_Impl* pImpl= static_cast<Thread_Impl*>(Thread);
918
919
0
    if (!pImpl)
920
0
    {
921
0
        SAL_WARN("sal.osl", "invalid osl_getThreadPriority(nullptr) call");
922
0
        return osl_Thread_PriorityUnknown; /* EINVAL */
923
0
    }
924
925
#ifndef NO_PTHREAD_PRIORITY
926
927
    if (pthread_getschedparam(pImpl->m_hThread, &Policy, &Param) != 0)
928
        return osl_Thread_PriorityUnknown; /* ESRCH */
929
930
    pthread_once (&(g_thread.m_once), osl_thread_priority_init_Impl);
931
932
    /* map pthread priority to enum */
933
    if (Param.sched_priority==g_thread.m_priority.m_Highest)
934
    {
935
        /* 127 - highest */
936
        Priority= osl_Thread_PriorityHighest;
937
    }
938
    else if (Param.sched_priority > g_thread.m_priority.m_Normal)
939
    {
940
        /* 65..126 - above normal */
941
        Priority= osl_Thread_PriorityAboveNormal;
942
    }
943
    else if (Param.sched_priority == g_thread.m_priority.m_Normal)
944
    {
945
        /* normal */
946
        Priority= osl_Thread_PriorityNormal;
947
    }
948
    else if (Param.sched_priority > g_thread.m_priority.m_Lowest)
949
    {
950
        /* 63..1 -below normal */
951
        Priority= osl_Thread_PriorityBelowNormal;
952
    }
953
    else if (Param.sched_priority == g_thread.m_priority.m_Lowest)
954
    {
955
        /* 0 - lowest */
956
        Priority= osl_Thread_PriorityLowest;
957
    }
958
    else
959
    {
960
        /* unknown */
961
        Priority= osl_Thread_PriorityUnknown;
962
    }
963
964
#endif /* NO_PTHREAD_PRIORITY */
965
966
0
    return Priority;
967
0
}
968
969
namespace {
970
971
struct wrapper_pthread_key
972
{
973
    pthread_key_t m_key;
974
    oslThreadKeyCallbackFunction pfnCallback;
975
};
976
977
}
978
979
oslThreadKey SAL_CALL osl_createThreadKey( oslThreadKeyCallbackFunction pCallback )
980
0
{
981
0
    wrapper_pthread_key *pKey = static_cast<wrapper_pthread_key*>(malloc(sizeof(wrapper_pthread_key)));
982
983
0
    if (pKey)
984
0
    {
985
0
        pKey->pfnCallback = pCallback;
986
987
0
        if (pthread_key_create(&(pKey->m_key), pKey->pfnCallback) != 0)
988
0
        {
989
0
            free(pKey);
990
0
            pKey = nullptr;
991
0
        }
992
0
    }
993
994
0
    return static_cast<oslThreadKey>(pKey);
995
0
}
996
997
void SAL_CALL osl_destroyThreadKey(oslThreadKey Key)
998
0
{
999
0
    wrapper_pthread_key *pKey = static_cast<wrapper_pthread_key*>(Key);
1000
0
    if (pKey)
1001
0
    {
1002
0
        pthread_key_delete(pKey->m_key);
1003
0
        free(pKey);
1004
0
    }
1005
0
}
1006
1007
void* SAL_CALL osl_getThreadKeyData(oslThreadKey Key)
1008
0
{
1009
0
    wrapper_pthread_key *pKey = static_cast<wrapper_pthread_key*>(Key);
1010
0
    return pKey ? pthread_getspecific(pKey->m_key) : nullptr;
1011
0
}
1012
1013
sal_Bool SAL_CALL osl_setThreadKeyData(oslThreadKey Key, void *pData)
1014
0
{
1015
0
    bool bRet;
1016
0
    void *pOldData = nullptr;
1017
0
    wrapper_pthread_key *pKey = static_cast<wrapper_pthread_key*>(Key);
1018
0
    if (!pKey)
1019
0
        return false;
1020
1021
0
    if (pKey->pfnCallback)
1022
0
        pOldData = pthread_getspecific(pKey->m_key);
1023
1024
0
    bRet = (pthread_setspecific(pKey->m_key, pData) == 0);
1025
1026
0
    if (bRet && pKey->pfnCallback && pOldData)
1027
0
        pKey->pfnCallback(pOldData);
1028
1029
0
    return bRet;
1030
0
}
1031
1032
rtl_TextEncoding getThreadTextEncodingForInitialization()
1033
309
{
1034
    /* determine default text encoding */
1035
309
    rtl_TextEncoding defaultEncoding = osl_getTextEncodingFromLocale(nullptr);
1036
    // Tools string functions call abort() on an unknown encoding so ASCII is a
1037
    // meaningful fallback:
1038
309
    if ( RTL_TEXTENCODING_DONTKNOW == defaultEncoding )
1039
0
    {
1040
0
        SAL_WARN("sal.osl", "RTL_TEXTENCODING_DONTKNOW -> _ASCII_US");
1041
0
        defaultEncoding = RTL_TEXTENCODING_ASCII_US;
1042
0
    }
1043
1044
309
    return defaultEncoding;
1045
309
}
1046
1047
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */