Coverage Report

Created: 2026-08-08 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/brpc/src/bthread/task_group.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
// bthread - An M:N threading library to make applications more concurrent.
19
20
// Date: Tue Jul 10 17:40:58 CST 2012
21
22
#include <sys/types.h>
23
#include <stddef.h>                         // size_t
24
#include <gflags/gflags.h>
25
#include "butil/compat.h"                   // OS_MACOSX
26
#include "butil/macros.h"                   // ARRAY_SIZE
27
#include "butil/scoped_lock.h"              // BAIDU_SCOPED_LOCK
28
#include "butil/fast_rand.h"
29
#include "butil/unique_ptr.h"
30
#include "butil/third_party/murmurhash3/murmurhash3.h" // fmix64
31
#include "butil/reloadable_flags.h"
32
#include "bthread/errno.h"                  // ESTOP
33
#include "bthread/butex.h"                  // butex_*
34
#include "bthread/sys_futex.h"              // futex_wake_private
35
#include "bthread/processor.h"              // cpu_relax
36
#include "bthread/task_control.h"
37
#include "bthread/task_group.h"
38
#include "bthread/timer_thread.h"
39
#include "bthread/bthread.h"
40
41
#ifdef __x86_64__
42
#include <x86intrin.h>
43
#endif // __x86_64__
44
45
#ifdef __ARM_NEON
46
#include <arm_neon.h>
47
#endif // __ARM_NEON
48
49
namespace bthread {
50
51
// Global span function pointers for bthread lifecycle tracing.
52
// These are set by brpc layer via bthread_set_span_funcs().
53
void* (*g_create_bthread_span)() = NULL;
54
void (*g_rpcz_parent_span_dtor)(void*) = NULL;
55
void (*g_end_bthread_span)() = NULL;
56
57
static const bthread_attr_t BTHREAD_ATTR_TASKGROUP = {
58
    BTHREAD_STACKTYPE_UNKNOWN, 0, NULL, BTHREAD_TAG_INVALID, {0} };
59
60
DEFINE_bool(show_bthread_creation_in_vars, false, "When this flags is on, The time "
61
            "from bthread creation to first run will be recorded and shown in /vars");
62
BUTIL_VALIDATE_GFLAG(show_bthread_creation_in_vars, butil::PassValidate);
63
64
DEFINE_bool(show_per_worker_usage_in_vars, false,
65
            "Show per-worker usage in /vars/bthread_per_worker_usage_<tid>");
66
BUTIL_VALIDATE_GFLAG(show_per_worker_usage_in_vars, butil::PassValidate);
67
68
DEFINE_bool(bthread_enable_cpu_clock_stat, false,
69
            "Enable CPU clock statistics for bthread");
70
BUTIL_VALIDATE_GFLAG(bthread_enable_cpu_clock_stat, butil::PassValidate);
71
72
BAIDU_VOLATILE_THREAD_LOCAL(TaskGroup*, tls_task_group, NULL);
73
// Sync with TaskMeta::local_storage when a bthread is created or destroyed.
74
// During running, the two fields may be inconsistent, use tls_bls as the
75
// groundtruth.
76
BAIDU_VOLATILE_THREAD_LOCAL(LocalStorage, tls_bls, BTHREAD_LOCAL_STORAGE_INITIALIZER);
77
78
// defined in bthread/key.cpp
79
extern void return_keytable(bthread_keytable_pool_t*, KeyTable*);
80
81
// [Hacky] This is a special TLS set by bthread-rpc privately... to save
82
// overhead of creation keytable, may be removed later.
83
BAIDU_VOLATILE_THREAD_LOCAL(void*, tls_unique_user_ptr, NULL);
84
85
const TaskStatistics EMPTY_STAT = { 0, 0, 0 };
86
87
0
AtomicInteger128::Value AtomicInteger128::load() const {
88
0
#ifdef __x86_64__
89
0
    (void)_mutex;
90
0
    (void)_seq;
91
0
    __m128i value = _mm_load_si128(reinterpret_cast<const __m128i*>(&_value));
92
0
    return {value[0], value[1]};
93
#elif defined(__ARM_NEON)
94
    (void)_mutex;
95
    (void)_seq;
96
    int64x2_t value = vld1q_s64(reinterpret_cast<const int64_t*>(&_value));
97
    return {value[0], value[1]};
98
#elif defined(__riscv) && __riscv_xlen == 64
99
    (void)_mutex;
100
    // RISC-V: Seqlock-based atomic 128-bit load.
101
    int64_t v1, v2;
102
    uint64_t seq0, seq1;
103
    do {
104
        __asm__ volatile(
105
            "ld %0, %1\n\t"
106
            : "=r"(seq0)
107
            : "m"(_seq)
108
            : "memory"
109
        );
110
        if (seq0 & 1) continue;
111
        __asm__ volatile("fence r, rw\n\t" ::: "memory");
112
        __asm__ volatile(
113
            "ld %0, %2\n\t"
114
            "ld %1, %3\n\t"
115
            : "=r"(v1), "=r"(v2)
116
            : "m"(_value.v1), "m"(_value.v2)
117
            : "memory"
118
        );
119
        __asm__ volatile("fence r, rw\n\t" ::: "memory");
120
        __asm__ volatile(
121
            "ld %0, %1\n\t"
122
            : "=r"(seq1)
123
            : "m"(_seq)
124
            : "memory"
125
        );
126
    } while (seq0 != seq1);
127
    return {v1, v2};
128
#else
129
    BAIDU_SCOPED_LOCK(const_cast<FastPthreadMutex&>(_mutex));
130
    return _value;
131
#endif
132
0
}
133
134
0
void AtomicInteger128::store(Value value) {
135
0
#ifdef __x86_64__
136
0
    (void)_seq;
137
0
    __m128i v = _mm_load_si128(reinterpret_cast<__m128i*>(&value));
138
0
    _mm_store_si128(reinterpret_cast<__m128i*>(&_value), v);
139
#elif defined(__ARM_NEON)
140
    (void)_seq;
141
    int64x2_t v = vld1q_s64(reinterpret_cast<int64_t*>(&value));
142
    vst1q_s64(reinterpret_cast<int64_t*>(&_value), v);
143
#elif defined(__riscv) && __riscv_xlen == 64
144
    (void)_mutex;
145
    // RISC-V: Seqlock-based atomic 128-bit store.
146
    uint64_t old_seq;
147
    __asm__ volatile(
148
        "ld %0, %1\n\t"
149
        : "=r"(old_seq)
150
        : "m"(_seq)
151
        : "memory"
152
    );
153
    uint64_t new_seq = old_seq + 1;
154
    __asm__ volatile(
155
        "fence w, w\n\t"
156
        "sd %1, %0\n\t"
157
        : "=m"(_seq)
158
        : "r"(new_seq)
159
        : "memory"
160
    );
161
    __asm__ volatile("fence w, w\n\t" ::: "memory");
162
    __asm__ volatile(
163
        "sd %2, %0\n\t"
164
        "sd %3, %1\n\t"
165
        : "=m"(_value.v1), "=m"(_value.v2)
166
        : "r"(value.v1), "r"(value.v2)
167
        : "memory"
168
    );
169
    __asm__ volatile("fence w, w\n\t" ::: "memory");
170
    new_seq++;
171
    __asm__ volatile(
172
        "sd %1, %0\n\t"
173
        : "=m"(_seq)
174
        : "r"(new_seq)
175
        : "memory"
176
    );
177
#else
178
    BAIDU_SCOPED_LOCK(const_cast<FastPthreadMutex&>(_mutex));
179
    _value = value;
180
#endif
181
0
}
182
183
184
0
int TaskGroup::get_attr(bthread_t tid, bthread_attr_t* out) {
185
0
    TaskMeta* const m = address_meta(tid);
186
0
    if (m != NULL) {
187
0
        const uint32_t given_ver = get_version(tid);
188
0
        BAIDU_SCOPED_LOCK(m->version_lock);
189
0
        if (given_ver == *m->version_butex) {
190
0
            *out = m->attr;
191
0
            return 0;
192
0
        }
193
0
    }
194
0
    errno = EINVAL;
195
0
    return -1;
196
0
}
197
198
0
void TaskGroup::set_stopped(bthread_t tid) {
199
0
    TaskMeta* const m = address_meta(tid);
200
0
    if (m != NULL) {
201
0
        const uint32_t given_ver = get_version(tid);
202
0
        BAIDU_SCOPED_LOCK(m->version_lock);
203
0
        if (given_ver == *m->version_butex) {
204
0
            m->stop = true;
205
0
        }
206
0
    }
207
0
}
208
209
0
bool TaskGroup::is_stopped(bthread_t tid) {
210
0
    TaskMeta* const m = address_meta(tid);
211
0
    if (m != NULL) {
212
0
        const uint32_t given_ver = get_version(tid);
213
0
        BAIDU_SCOPED_LOCK(m->version_lock);
214
0
        if (given_ver == *m->version_butex) {
215
0
            return m->stop;
216
0
        }
217
0
    }
218
    // If the tid does not exist or version does not match, it's intuitive
219
    // to treat the thread as "stopped".
220
0
    return true;
221
0
}
222
223
0
bool TaskGroup::wait_task(bthread_t* tid) {
224
0
    do {
225
0
#ifndef BTHREAD_DONT_SAVE_PARKING_STATE
226
0
        if (_last_pl_state.stopped()) {
227
0
            return false;
228
0
        }
229
0
        _pl->wait(_last_pl_state);
230
0
        if (steal_task(tid)) {
231
0
            return true;
232
0
        }
233
#else
234
        const ParkingLot::State st = _pl->get_state();
235
        if (st.stopped()) {
236
            return false;
237
        }
238
        if (steal_task(tid)) {
239
            return true;
240
        }
241
        _pl->wait(st);
242
#endif
243
0
    } while (true);
244
0
}
245
246
0
static double get_cumulated_cputime_from_this(void* arg) {
247
0
    return static_cast<TaskGroup*>(arg)->cumulated_cputime_ns() / 1000000000.0;
248
0
}
249
250
0
int64_t TaskGroup::cumulated_cputime_ns() const {
251
0
    CPUTimeStat cpu_time_stat = _cpu_time_stat.load();
252
    // Add the elapsed time of running bthread.
253
0
    int64_t cumulated_cputime_ns = cpu_time_stat.cumulated_cputime_ns();
254
0
    if (!cpu_time_stat.is_main_task()) {
255
0
        cumulated_cputime_ns += butil::cpuwide_time_ns() - cpu_time_stat.last_run_ns();
256
0
    }
257
0
    return cumulated_cputime_ns;
258
0
}
259
260
0
void TaskGroup::run_main_task() {
261
0
    bvar::PassiveStatus<double> cumulated_cputime(
262
0
        get_cumulated_cputime_from_this, this);
263
0
    std::unique_ptr<bvar::PerSecond<bvar::PassiveStatus<double> > > usage_bvar;
264
265
0
    TaskGroup* dummy = this;
266
0
    bthread_t tid;
267
0
    while (wait_task(&tid)) {
268
0
        sched_to(&dummy, tid);
269
0
        DCHECK_EQ(this, dummy);
270
0
        DCHECK_EQ(_cur_meta->stack, _main_stack);
271
0
        if (_cur_meta->tid != _main_tid) {
272
0
            task_runner(1/*skip remained*/);
273
0
        }
274
0
        if (FLAGS_show_per_worker_usage_in_vars && !usage_bvar) {
275
0
            char name[32];
276
#if defined(OS_MACOSX)
277
            snprintf(name, sizeof(name), "bthread_worker_usage_%" PRIu64,
278
                     pthread_numeric_id());
279
#else
280
0
            snprintf(name, sizeof(name), "bthread_worker_usage_%ld",
281
0
                     (long)syscall(SYS_gettid));
282
0
#endif
283
0
            usage_bvar.reset(new bvar::PerSecond<bvar::PassiveStatus<double> >
284
0
                             (name, &cumulated_cputime, 1));
285
0
        }
286
0
    }
287
    // Don't forget to add elapse of last wait_task.
288
0
    current_task()->stat.cputime_ns +=
289
0
        butil::cpuwide_time_ns() - _cpu_time_stat.load_unsafe().last_run_ns();
290
0
}
291
292
TaskGroup::TaskGroup(TaskControl* c)
293
0
    :  _control(c) {
294
0
    CHECK(c);
295
0
}
296
297
0
TaskGroup::~TaskGroup() {
298
0
    if (_main_tid) {
299
0
        TaskMeta* m = address_meta(_main_tid);
300
0
        CHECK(_main_stack == m->stack);
301
#ifdef BUTIL_USE_ASAN
302
        _main_stack->storage.bottom = NULL;
303
        _main_stack->storage.stacksize = 0;
304
#endif // BUTIL_USE_ASAN
305
0
        return_stack(m->release_stack());
306
0
        return_resource(get_slot(_main_tid));
307
0
        _main_tid = 0;
308
0
    }
309
0
}
310
311
#ifdef BUTIL_USE_ASAN
312
// Returns the **highest** address of the calling pthread's stack and its
313
// total size, matching brpc's `StackStorage::bottom` convention (see comment
314
// in bthread/stack.h: "Assume stack grows upwards"). Note that on Linux
315
// `pthread_attr_getstack(3)` returns the lowest address of the region, so
316
// we have to translate it; on macOS `pthread_get_stackaddr_np(3)` already
317
// returns the stack base (highest address), so we use it as-is.
318
int PthreadAttrGetStack(void*& stack_addr, size_t& stack_size) {
319
#if defined(OS_MACOSX)
320
    stack_addr = pthread_get_stackaddr_np(pthread_self());
321
    stack_size = pthread_get_stacksize_np(pthread_self());
322
    return 0;
323
#else
324
    pthread_attr_t attr;
325
    int rc = pthread_getattr_np(pthread_self(), &attr);
326
    if (0 != rc) {
327
        LOG(ERROR) << "Fail to get pthread attributes: " << berror(rc);
328
        return rc;
329
    }
330
    void* stack_lowest = NULL;
331
    rc = pthread_attr_getstack(&attr, &stack_lowest, &stack_size);
332
    if (0 != rc) {
333
        LOG(ERROR) << "Fail to get pthread stack: " << berror(rc);
334
    } else {
335
        // Translate lowest -> highest to match StackStorage::bottom.
336
        stack_addr = (char*)stack_lowest + stack_size;
337
    }
338
    pthread_attr_destroy(&attr);
339
    return rc;
340
#endif // OS_MACOSX
341
}
342
#endif // BUTIL_USE_ASAN
343
344
0
int TaskGroup::init(size_t runqueue_capacity) {
345
0
    if (_rq.init(runqueue_capacity) != 0) {
346
0
        LOG(FATAL) << "Fail to init _rq";
347
0
        return -1;
348
0
    }
349
0
    if (_remote_rq.init(runqueue_capacity / 2) != 0) {
350
0
        LOG(FATAL) << "Fail to init _remote_rq";
351
0
        return -1;
352
0
    }
353
354
#ifdef BUTIL_USE_ASAN
355
    void* stack_addr = NULL;
356
    size_t stack_size = 0;
357
    if (0 != PthreadAttrGetStack(stack_addr, stack_size)) {
358
        return -1;
359
    }
360
#endif // BUTIL_USE_ASAN
361
362
0
    ContextualStack* stk = get_stack(STACK_TYPE_MAIN, NULL);
363
0
    if (NULL == stk) {
364
0
        LOG(FATAL) << "Fail to get main stack container";
365
0
        return -1;
366
0
    }
367
0
    butil::ResourceId<TaskMeta> slot;
368
0
    TaskMeta* m = butil::get_resource<TaskMeta>(&slot);
369
0
    if (NULL == m) {
370
0
        LOG(FATAL) << "Fail to get TaskMeta";
371
0
        return -1;
372
0
    }
373
0
    m->sleep_failed = false;
374
0
    m->stop = false;
375
0
    m->interrupted = false;
376
0
    m->about_to_quit = false;
377
0
    m->fn = NULL;
378
0
    m->arg = NULL;
379
0
    m->local_storage = LOCAL_STORAGE_INIT;
380
0
    m->cpuwide_start_ns = butil::cpuwide_time_ns();
381
0
    m->stat = EMPTY_STAT;
382
0
    m->attr = BTHREAD_ATTR_TASKGROUP;
383
0
    m->tid = make_tid(*m->version_butex, slot);
384
0
    m->set_stack(stk);
385
386
#ifdef BUTIL_USE_ASAN
387
    stk->storage.bottom = stack_addr;
388
    stk->storage.stacksize = stack_size;
389
    // No guard size required for ASan.
390
#endif // BUTIL_USE_ASAN
391
392
0
    _cur_meta = m;
393
0
    _main_tid = m->tid;
394
0
    _main_stack = stk;
395
396
0
    CPUTimeStat cpu_time_stat;
397
0
    cpu_time_stat.set_last_run_ns(m->cpuwide_start_ns, true);
398
0
    _cpu_time_stat.store(cpu_time_stat);
399
0
    _last_cpu_clock_ns = 0;
400
401
0
    return 0;
402
0
}
403
404
#ifdef BUTIL_USE_ASAN
405
void TaskGroup::asan_task_runner(intptr_t) {
406
    // This is a new thread, and it doesn't have the fake stack yet. ASan will
407
    // create it lazily, for now just pass NULL.
408
    internal::FinishSwitchFiber(NULL);
409
    task_runner(0);
410
}
411
#endif // BUTIL_USE_ASAN
412
413
0
void TaskGroup::task_runner(intptr_t skip_remained) {
414
    // NOTE: tls_task_group is volatile since tasks are moved around
415
    //       different groups.
416
0
    TaskGroup* g = BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_task_group);
417
#ifdef BRPC_BTHREAD_TRACER
418
    TaskTracer::set_running_status(g->tid(), g->_cur_meta);
419
#endif // BRPC_BTHREAD_TRACER
420
421
0
    if (!skip_remained) {
422
0
        while (g->_last_context_remained) {
423
0
            RemainedFn fn = g->_last_context_remained;
424
0
            g->_last_context_remained = NULL;
425
0
            fn(g->_last_context_remained_arg);
426
0
            g = BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_task_group);
427
0
        }
428
429
#ifndef NDEBUG
430
        --g->_sched_recursive_guard;
431
#endif
432
0
    }
433
434
0
    do {
435
        // A task can be stopped before it gets running, in which case
436
        // we may skip user function, but that may confuse user:
437
        // Most tasks have variables to remember running result of the task,
438
        // which is often initialized to values indicating success. If an
439
        // user function is never called, the variables will be unchanged
440
        // however they'd better reflect failures because the task is stopped
441
        // abnormally.
442
443
        // Meta and identifier of the task is persistent in this run.
444
0
        TaskMeta* const m = g->_cur_meta;
445
446
0
        if (FLAGS_show_bthread_creation_in_vars) {
447
            // NOTE: the thread triggering exposure of pending time may spend
448
            // considerable time because a single bvar::LatencyRecorder
449
            // contains many bvar.
450
0
            g->_control->exposed_pending_time() <<
451
0
                (butil::cpuwide_time_ns() - m->cpuwide_start_ns) / 1000L;
452
0
        }
453
454
        // Not catch exceptions except ExitException which is for implementing
455
        // bthread_exit(). User code is intended to crash when an exception is
456
        // not caught explicitly. This is consistent with other threading
457
        // libraries.
458
0
        void* thread_return;
459
0
        try {
460
0
            thread_return = m->fn(m->arg);
461
0
        } catch (ExitException& e) {
462
0
            thread_return = e.value();
463
0
        }
464
465
0
        if (m->attr.flags & BTHREAD_INHERIT_SPAN) {
466
0
            if (g_end_bthread_span) {
467
0
                g_end_bthread_span();
468
0
            }
469
0
        }
470
471
        // TODO: Save thread_return
472
0
        (void)thread_return;
473
474
        // Logging must be done before returning the keytable, since the logging lib
475
        // use bthread local storage internally, or will cause memory leak.
476
        // FIXME: the time from quiting fn to here is not counted into cputime
477
0
        if (m->attr.flags & BTHREAD_LOG_START_AND_FINISH) {
478
0
            LOG(INFO) << "Finished bthread " << m->tid << ", cputime="
479
0
                      << m->stat.cputime_ns / 1000000.0 << "ms";
480
0
        }
481
482
        // Clean up span if it exists. This must be done before keytable cleanup
483
        // because span cleanup may use bthread local storage (e.g. logging,
484
        // which allocates bthread-local stream arrays via bthread_setspecific).
485
        // If span cleanup ran after keytable cleanup, such allocations would
486
        // re-populate the keytable and never be reclaimed, causing memory leak.
487
0
        LocalStorage* tls_bls_ptr = bthread::tls_bls_ptr();
488
0
        if (tls_bls_ptr->rpcz_parent_span && g_rpcz_parent_span_dtor) {
489
0
            g_rpcz_parent_span_dtor(tls_bls_ptr->rpcz_parent_span);
490
0
            tls_bls_ptr = bthread::tls_bls_ptr();
491
0
            tls_bls_ptr->rpcz_parent_span = NULL;
492
0
            m->local_storage.rpcz_parent_span = NULL;
493
0
        }
494
495
        // Clean tls variables, must be done before changing version_butex
496
        // otherwise another thread just joined this thread may not see side
497
        // effects of destructing tls variables.
498
0
        KeyTable* kt = tls_bls_ptr->keytable;
499
0
        if (kt != NULL) {
500
0
            return_keytable(m->attr.keytable_pool, kt);
501
            // After deletion: tls may be set during deletion.
502
0
            tls_bls_ptr = bthread::tls_bls_ptr();
503
0
            tls_bls_ptr->keytable = NULL;
504
0
            m->local_storage.keytable = NULL; // optional
505
0
        }
506
507
        // During running the function in TaskMeta and deleting the KeyTable in
508
        // return_KeyTable, the group is probably changed.
509
0
        g =  BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_task_group);
510
511
        // Increase the version and wake up all joiners, if resulting version
512
        // is 0, change it to 1 to make bthread_t never be 0. Any access
513
        // or join to the bthread after changing version will be rejected.
514
        // The spinlock is for visibility of TaskGroup::get_attr.
515
#ifdef BRPC_BTHREAD_TRACER
516
        bool tracing = false;
517
#endif // BRPC_BTHREAD_TRACER
518
0
        {
519
0
            BAIDU_SCOPED_LOCK(m->version_lock);
520
#ifdef BRPC_BTHREAD_TRACER
521
            tracing = TaskTracer::set_end_status_unsafe(m);
522
#endif // BRPC_BTHREAD_TRACER
523
0
            if (0 == ++*m->version_butex) {
524
0
                ++*m->version_butex;
525
0
            }
526
0
        }
527
0
        butex_wake_except(m->version_butex, 0);
528
529
#ifdef BRPC_BTHREAD_TRACER
530
        if (tracing) {
531
            // Wait for tracing completion.
532
            g->_control->_task_tracer.WaitForTracing(m);
533
        }
534
        g->_control->_task_tracer.set_status(TASK_STATUS_UNKNOWN, m);
535
#endif // BRPC_BTHREAD_TRACER
536
537
0
        g->_control->_nbthreads << -1;
538
0
        g->_control->tag_nbthreads(g->tag()) << -1;
539
0
        g->set_remained(_release_last_context, m);
540
0
        ending_sched(&g);
541
542
0
    } while (g->_cur_meta->tid != g->_main_tid);
543
544
    // Was called from a pthread and we don't have BTHREAD_STACKTYPE_PTHREAD
545
    // tasks to run, quit for more tasks.
546
0
}
547
548
0
void TaskGroup::_release_last_context(void* arg) {
549
0
    TaskMeta* m = static_cast<TaskMeta*>(arg);
550
0
    if (m->stack_type() != STACK_TYPE_PTHREAD) {
551
0
        return_stack(m->release_stack()/*may be NULL*/);
552
0
    } else {
553
        // it's _main_stack, don't return.
554
0
        m->set_stack(NULL);
555
0
    }
556
0
    return_resource(get_slot(m->tid));
557
0
}
558
559
int TaskGroup::start_foreground(TaskGroup** pg,
560
                                bthread_t* __restrict th,
561
                                const bthread_attr_t* __restrict attr,
562
                                void * (*fn)(void*),
563
0
                                void* __restrict arg) {
564
0
    if (__builtin_expect(!fn, 0)) {
565
0
        return EINVAL;
566
0
    }
567
0
    const int64_t start_ns = butil::cpuwide_time_ns();
568
0
    const bthread_attr_t using_attr = (attr ? *attr : BTHREAD_ATTR_NORMAL);
569
0
    butil::ResourceId<TaskMeta> slot;
570
0
    TaskMeta* m = butil::get_resource(&slot);
571
0
    if (BAIDU_UNLIKELY(NULL == m)) {
572
0
        return ENOMEM;
573
0
    }
574
0
    CHECK(m->current_waiter.load(butil::memory_order_relaxed) == NULL);
575
0
    m->sleep_failed = false;
576
0
    m->stop = false;
577
0
    m->interrupted = false;
578
0
    m->about_to_quit = false;
579
0
    m->fn = fn;
580
0
    m->arg = arg;
581
0
    CHECK(m->stack == NULL);
582
0
    m->attr = using_attr;
583
0
    m->local_storage = LOCAL_STORAGE_INIT;
584
0
    if (using_attr.flags & BTHREAD_INHERIT_SPAN) {
585
0
        if (g_create_bthread_span) {
586
0
            m->local_storage.rpcz_parent_span = g_create_bthread_span();
587
0
        } else {
588
0
            m->local_storage.rpcz_parent_span = BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_bls).rpcz_parent_span;
589
0
        }
590
0
    }
591
0
    m->cpuwide_start_ns = start_ns;
592
0
    m->stat = EMPTY_STAT;
593
0
    m->tid = make_tid(*m->version_butex, slot);
594
595
0
    TaskGroup* g = *pg;
596
0
    m->priority_index = g->_cur_meta->priority_index;
597
0
    m->attr.tag = g->tag();
598
0
    *th = m->tid;
599
0
    if (using_attr.flags & BTHREAD_LOG_START_AND_FINISH) {
600
0
        LOG(INFO) << "Started bthread " << m->tid;
601
0
    }
602
603
0
    g->_control->_nbthreads << 1;
604
0
    g->_control->tag_nbthreads(g->tag()) << 1;
605
#ifdef BRPC_BTHREAD_TRACER
606
    g->_control->_task_tracer.set_status(TASK_STATUS_CREATED, m);
607
#endif // BRPC_BTHREAD_TRACER
608
0
    if (g->is_current_pthread_task()) {
609
        // never create foreground task in pthread.
610
0
        g->ready_to_run(m, using_attr.flags & BTHREAD_NOSIGNAL);
611
0
    } else {
612
        // NOSIGNAL affects current task, not the new task.
613
0
        RemainedFn fn = NULL;
614
0
        auto& cur_attr = g->_cur_meta->attr;
615
0
        if (g->_control->_enable_priority_queue && cur_attr.flags & BTHREAD_GLOBAL_PRIORITY) {
616
0
            fn = priority_to_run;
617
0
        } else if (g->current_task()->about_to_quit) {
618
0
            fn = ready_to_run_in_worker_ignoresignal;
619
0
        } else {
620
0
            fn = ready_to_run_in_worker;
621
0
        }
622
0
        ReadyToRunArgs args = {
623
0
            g->tag(), g->_cur_meta, (bool)(using_attr.flags & BTHREAD_NOSIGNAL)
624
0
        };
625
0
        g->set_remained(fn, &args);
626
0
        sched_to(pg, m->tid);
627
0
    }
628
0
    return 0;
629
0
}
630
631
template <bool REMOTE>
632
int TaskGroup::start_background(bthread_t* __restrict th,
633
                                const bthread_attr_t* __restrict attr,
634
                                void * (*fn)(void*),
635
0
                                void* __restrict arg) {
636
0
    if (__builtin_expect(!fn, 0)) {
637
0
        return EINVAL;
638
0
    }
639
0
    const int64_t start_ns = butil::cpuwide_time_ns();
640
0
    const bthread_attr_t using_attr = (attr ? *attr : BTHREAD_ATTR_NORMAL);
641
0
    butil::ResourceId<TaskMeta> slot;
642
0
    TaskMeta* m = butil::get_resource(&slot);
643
0
    if (BAIDU_UNLIKELY(NULL == m)) {
644
0
        return ENOMEM;
645
0
    }
646
0
    CHECK(m->current_waiter.load(butil::memory_order_relaxed) == NULL);
647
0
    m->sleep_failed = false;
648
0
    m->stop = false;
649
0
    m->interrupted = false;
650
0
    m->about_to_quit = false;
651
0
    m->fn = fn;
652
0
    m->arg = arg;
653
0
    CHECK(m->stack == NULL);
654
0
    m->attr = using_attr;
655
0
    m->local_storage = LOCAL_STORAGE_INIT;
656
0
    if (using_attr.flags & BTHREAD_INHERIT_SPAN) {
657
0
        if (g_create_bthread_span) {
658
0
            m->local_storage.rpcz_parent_span = g_create_bthread_span();
659
0
        } else {
660
0
            m->local_storage.rpcz_parent_span = BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_bls).rpcz_parent_span;
661
0
        }
662
0
    }
663
0
    m->cpuwide_start_ns = start_ns;
664
0
    m->stat = EMPTY_STAT;
665
0
    m->tid = make_tid(*m->version_butex, slot);
666
0
    m->priority_index = _cur_meta->priority_index;
667
0
    *th = m->tid;
668
0
    if (using_attr.flags & BTHREAD_LOG_START_AND_FINISH) {
669
0
        LOG(INFO) << "Started bthread " << m->tid;
670
0
    }
671
0
    m->attr.tag = tag();
672
0
    _control->_nbthreads << 1;
673
0
    _control->tag_nbthreads(tag()) << 1;
674
#ifdef BRPC_BTHREAD_TRACER
675
    _control->_task_tracer.set_status(TASK_STATUS_CREATED, m);
676
#endif // BRPC_BTHREAD_TRACER
677
0
    if (REMOTE) {
678
0
        ready_to_run_remote(m, (using_attr.flags & BTHREAD_NOSIGNAL));
679
0
    } else {
680
0
        ready_to_run(m, (using_attr.flags & BTHREAD_NOSIGNAL));
681
0
    }
682
0
    return 0;
683
0
}
Unexecuted instantiation: int bthread::TaskGroup::start_background<true>(unsigned long*, bthread_attr_t const*, void* (*)(void*), void*)
Unexecuted instantiation: int bthread::TaskGroup::start_background<false>(unsigned long*, bthread_attr_t const*, void* (*)(void*), void*)
684
685
// Explicit instantiations.
686
template int
687
TaskGroup::start_background<true>(bthread_t* __restrict th,
688
                                  const bthread_attr_t* __restrict attr,
689
                                  void * (*fn)(void*),
690
                                  void* __restrict arg);
691
template int
692
TaskGroup::start_background<false>(bthread_t* __restrict th,
693
                                   const bthread_attr_t* __restrict attr,
694
                                   void * (*fn)(void*),
695
                                   void* __restrict arg);
696
697
0
int TaskGroup::join(bthread_t tid, void** return_value) {
698
0
    if (__builtin_expect(!tid, 0)) {  // tid of bthread is never 0.
699
0
        return EINVAL;
700
0
    }
701
0
    TaskMeta* m = address_meta(tid);
702
0
    if (BAIDU_UNLIKELY(NULL == m)) {
703
        // The bthread is not created yet, this join is definitely wrong.
704
0
        return EINVAL;
705
0
    }
706
0
    TaskGroup* g = BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_task_group);
707
0
    if (g != NULL && g->current_tid() == tid) {
708
        // joining self causes indefinite waiting.
709
0
        return EINVAL;
710
0
    }
711
0
    const uint32_t expected_version = get_version(tid);
712
0
    while (*m->version_butex == expected_version) {
713
0
        if (butex_wait(m->version_butex, expected_version, NULL) < 0 &&
714
0
            errno != EWOULDBLOCK && errno != EINTR) {
715
0
            return errno;
716
0
        }
717
0
    }
718
    // Ensure all memory writes made by the joined bthread are visible to
719
    // the joining thread after join returns. This matches the semantic
720
    // guarantee provided by pthread_join() across supported architectures.
721
0
    butil::atomic_thread_fence(butil::memory_order_acquire);
722
0
    if (return_value) {
723
0
        *return_value = NULL;
724
0
    }
725
0
    return 0;
726
0
}
727
728
0
bool TaskGroup::exists(bthread_t tid) {
729
0
    if (tid != 0) {  // tid of bthread is never 0.
730
0
        TaskMeta* m = address_meta(tid);
731
0
        if (m != NULL) {
732
0
            return (*m->version_butex == get_version(tid));
733
0
        }
734
0
    }
735
0
    return false;
736
0
}
737
738
0
TaskStatistics TaskGroup::main_stat() const {
739
0
    TaskMeta* m = address_meta(_main_tid);
740
0
    return m ? m->stat : EMPTY_STAT;
741
0
}
742
743
0
void TaskGroup::ending_sched(TaskGroup** pg) {
744
0
    TaskGroup* g = *pg;
745
0
    bthread_t next_tid = 0;
746
    // Find next task to run, if none, switch to idle thread of the group.
747
748
0
#ifndef BTHREAD_FAIR_WSQ
749
    // When BTHREAD_FAIR_WSQ is defined, profiling shows that cpu cost of
750
    // WSQ::steal() in example/multi_threaded_echo_c++ changes from 1.9%
751
    // to 2.9%
752
0
    const bool popped = g->_rq.pop(&next_tid);
753
#else
754
    const bool popped = g->_rq.steal(&next_tid);
755
#endif
756
0
    if (!popped && !g->steal_task(&next_tid)) {
757
        // Jump to main task if there's no task to run.
758
0
        next_tid = g->_main_tid;
759
0
    }
760
761
0
    TaskMeta* const cur_meta = g->_cur_meta;
762
0
    TaskMeta* next_meta = address_meta(next_tid);
763
0
    if (next_meta->stack == NULL) {
764
0
        if (next_meta->stack_type() == cur_meta->stack_type()) {
765
            // Reuse the stack of the current ending task.
766
            //
767
            // also works with pthread_task scheduling to pthread_task, the
768
            // transfered stack is just _main_stack.
769
0
            next_meta->set_stack(cur_meta->release_stack());
770
0
        } else {
771
#ifdef BUTIL_USE_ASAN
772
            ContextualStack* stk = get_stack(
773
                next_meta->stack_type(), asan_task_runner);
774
#else
775
0
            ContextualStack* stk = get_stack(next_meta->stack_type(), task_runner);
776
0
#endif // BUTIL_USE_ASAN
777
0
            if (stk) {
778
0
                next_meta->set_stack(stk);
779
0
            } else {
780
                // stack_type is BTHREAD_STACKTYPE_PTHREAD or out of memory,
781
                // In latter case, attr is forced to be BTHREAD_STACKTYPE_PTHREAD.
782
                // This basically means that if we can't allocate stack, run
783
                // the task in pthread directly.
784
0
                next_meta->attr.stack_type = BTHREAD_STACKTYPE_PTHREAD;
785
0
                next_meta->set_stack(g->_main_stack);
786
0
            }
787
0
        }
788
0
    }
789
0
    sched_to(pg, next_meta);
790
0
}
791
792
0
void TaskGroup::sched(TaskGroup** pg) {
793
0
    TaskGroup* g = *pg;
794
0
    bthread_t next_tid = 0;
795
    // Find next task to run, if none, switch to idle thread of the group.
796
0
#ifndef BTHREAD_FAIR_WSQ
797
0
    const bool popped = g->_rq.pop(&next_tid);
798
#else
799
    const bool popped = g->_rq.steal(&next_tid);
800
#endif
801
0
    if (!popped && !g->steal_task(&next_tid)) {
802
        // Jump to main task if there's no task to run.
803
0
        next_tid = g->_main_tid;
804
0
    }
805
0
    sched_to(pg, next_tid);
806
0
}
807
808
extern void CheckBthreadScheSafety();
809
810
0
void TaskGroup::sched_to(TaskGroup** pg, TaskMeta* next_meta) {
811
0
    TaskGroup* g = *pg;
812
#ifndef NDEBUG
813
    if ((++g->_sched_recursive_guard) > 1) {
814
        LOG(FATAL) << "Recursively(" << g->_sched_recursive_guard - 1
815
                   << ") call sched_to(" << g << ")";
816
    }
817
#endif
818
    // Save errno so that errno is bthread-specific.
819
0
    int saved_errno = errno;
820
0
    void* saved_unique_user_ptr = BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_unique_user_ptr);
821
822
0
    TaskMeta* const cur_meta = g->_cur_meta;
823
0
    int64_t now = butil::cpuwide_time_ns();
824
0
    CPUTimeStat cpu_time_stat = g->_cpu_time_stat.load_unsafe();
825
0
    int64_t elp_ns = now - cpu_time_stat.last_run_ns();
826
0
    cur_meta->stat.cputime_ns += elp_ns;
827
    // Update cpu_time_stat.
828
0
    cpu_time_stat.set_last_run_ns(now, is_main_task(g, next_meta->tid));
829
0
    cpu_time_stat.add_cumulated_cputime_ns(elp_ns, is_main_task(g, cur_meta->tid));
830
0
    g->_cpu_time_stat.store(cpu_time_stat);
831
832
0
    if (FLAGS_bthread_enable_cpu_clock_stat) {
833
0
        const int64_t cpu_thread_time = butil::cputhread_time_ns();
834
0
        if (g->_last_cpu_clock_ns != 0) {
835
0
            cur_meta->stat.cpu_usage_ns += cpu_thread_time - g->_last_cpu_clock_ns;
836
0
        }
837
0
        g->_last_cpu_clock_ns = cpu_thread_time;
838
0
    } else {
839
0
        g->_last_cpu_clock_ns = 0;
840
0
    }
841
842
0
    ++cur_meta->stat.nswitch;
843
0
    ++ g->_nswitch;
844
    // Switch to the task
845
0
    if (__builtin_expect(next_meta != cur_meta, 1)) {
846
0
        g->_cur_meta = next_meta;
847
        // Switch tls_bls
848
0
        cur_meta->local_storage = BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_bls);
849
0
        BAIDU_SET_VOLATILE_THREAD_LOCAL(tls_bls, next_meta->local_storage);
850
851
        // Logging must be done after switching the local storage, since the logging lib
852
        // use bthread local storage internally, or will cause memory leak.
853
0
        if ((cur_meta->attr.flags & BTHREAD_LOG_CONTEXT_SWITCH) ||
854
0
            (next_meta->attr.flags & BTHREAD_LOG_CONTEXT_SWITCH)) {
855
0
            LOG(INFO) << "Switch bthread: " << cur_meta->tid << " -> "
856
0
                      << next_meta->tid;
857
0
        }
858
859
0
        if (cur_meta->stack != NULL) {
860
0
            if (next_meta->stack != cur_meta->stack) {
861
0
                CheckBthreadScheSafety();
862
#ifdef BRPC_BTHREAD_TRACER
863
                g->_control->_task_tracer.set_status(TASK_STATUS_JUMPING, cur_meta);
864
                g->_control->_task_tracer.set_status(TASK_STATUS_JUMPING, next_meta);
865
#endif // BRPC_BTHREAD_TRACER
866
0
                {
867
0
                    BTHREAD_SCOPED_ASAN_FIBER_SWITCHER(next_meta->stack->storage);
868
0
                    jump_stack(cur_meta->stack, next_meta->stack);
869
0
                }
870
                // probably went to another group, need to assign g again.
871
0
                g = BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_task_group);
872
#ifdef BRPC_BTHREAD_TRACER
873
                TaskTracer::set_running_status(g->tid(), g->_cur_meta);
874
#endif // BRPC_BTHREAD_TRACER
875
0
            }
876
#ifndef NDEBUG
877
            else {
878
                // else pthread_task is switching to another pthread_task, sc
879
                // can only equal when they're both _main_stack
880
                CHECK(cur_meta->stack == g->_main_stack);
881
            }
882
#endif
883
0
        } /* else because of ending_sched(including pthread_task->pthread_task). */
884
#ifdef BRPC_BTHREAD_TRACER
885
        else {
886
            // _cur_meta: TASK_STATUS_FIRST_READY -> TASK_STATUS_RUNNING.
887
            TaskTracer::set_running_status(g->tid(), g->_cur_meta);
888
        }
889
#endif // BRPC_BTHREAD_TRACER
890
0
    } else {
891
0
        LOG(FATAL) << "bthread=" << g->current_tid() << " sched_to itself!";
892
0
    }
893
894
0
    while (g->_last_context_remained) {
895
0
        RemainedFn fn = g->_last_context_remained;
896
0
        g->_last_context_remained = NULL;
897
0
        fn(g->_last_context_remained_arg);
898
0
        g = BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_task_group);
899
0
    }
900
901
    // Restore errno
902
0
    errno = saved_errno;
903
    // tls_unique_user_ptr probably changed.
904
0
    BAIDU_SET_VOLATILE_THREAD_LOCAL(tls_unique_user_ptr, saved_unique_user_ptr);
905
906
#ifndef NDEBUG
907
    --g->_sched_recursive_guard;
908
#endif
909
0
    *pg = g;
910
0
}
911
912
0
void TaskGroup::destroy_self() {
913
0
    if (_control) {
914
0
        _control->_destroy_group(this);
915
0
        _control = NULL;
916
0
    } else {
917
0
        CHECK(false);
918
0
    }
919
0
}
920
921
922
0
void TaskGroup::ready_to_run(TaskMeta* meta, bool nosignal) {
923
#ifdef BRPC_BTHREAD_TRACER
924
    _control->_task_tracer.set_status(TASK_STATUS_READY, meta);
925
#endif // BRPC_BTHREAD_TRACER
926
0
    push_rq(meta->tid);
927
0
    if (nosignal) {
928
0
        ++_num_nosignal;
929
0
    } else {
930
0
        const int additional_signal = _num_nosignal;
931
0
        _num_nosignal = 0;
932
0
        _nsignaled += 1 + additional_signal;
933
0
        _control->signal_task(1 + additional_signal, _tag);
934
0
    }
935
0
}
936
937
0
void TaskGroup::flush_nosignal_tasks() {
938
0
    const int val = _num_nosignal;
939
0
    if (val) {
940
0
        _num_nosignal = 0;
941
0
        _nsignaled += val;
942
0
        _control->signal_task(val, _tag);
943
0
    }
944
0
}
945
946
0
void TaskGroup::ready_to_run_remote(TaskMeta* meta, bool nosignal) {
947
#ifdef BRPC_BTHREAD_TRACER
948
    _control->_task_tracer.set_status(TASK_STATUS_READY, meta);
949
#endif // BRPC_BTHREAD_TRACER
950
0
    _remote_rq._mutex.lock();
951
0
    while (!_remote_rq.push_locked(meta->tid)) {
952
0
        flush_nosignal_tasks_remote_locked(_remote_rq._mutex);
953
0
        LOG_EVERY_SECOND(ERROR) << "_remote_rq is full, capacity="
954
0
                                << _remote_rq.capacity();
955
0
        ::usleep(1000);
956
0
        _remote_rq._mutex.lock();
957
0
    }
958
0
    if (nosignal) {
959
0
        ++_remote_num_nosignal;
960
0
        _remote_rq._mutex.unlock();
961
0
    } else {
962
0
        const int additional_signal = _remote_num_nosignal;
963
0
        _remote_num_nosignal = 0;
964
0
        _remote_nsignaled += 1 + additional_signal;
965
0
        _remote_rq._mutex.unlock();
966
0
        _control->signal_task(1 + additional_signal, _tag);
967
0
    }
968
0
}
969
970
0
void TaskGroup::flush_nosignal_tasks_remote_locked(butil::Mutex& locked_mutex) {
971
0
    const int val = _remote_num_nosignal;
972
0
    if (!val) {
973
0
        locked_mutex.unlock();
974
0
        return;
975
0
    }
976
0
    _remote_num_nosignal = 0;
977
0
    _remote_nsignaled += val;
978
0
    locked_mutex.unlock();
979
0
    _control->signal_task(val, _tag);
980
0
}
981
982
0
void TaskGroup::ready_to_run_general(TaskMeta* meta, bool nosignal) {
983
0
    if (BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_task_group) == this) {
984
0
        return ready_to_run(meta, nosignal);
985
0
    }
986
0
    return ready_to_run_remote(meta, nosignal);
987
0
}
988
989
0
void TaskGroup::flush_nosignal_tasks_general() {
990
0
    if (BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_task_group) == this) {
991
0
        return flush_nosignal_tasks();
992
0
    }
993
0
    return flush_nosignal_tasks_remote();
994
0
}
995
996
0
void TaskGroup::ready_to_run_in_worker(void* args_in) {
997
0
    ReadyToRunArgs* args = static_cast<ReadyToRunArgs*>(args_in);
998
0
    return BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_task_group)->
999
0
        ready_to_run(args->meta, args->nosignal);
1000
0
}
1001
1002
0
void TaskGroup::ready_to_run_in_worker_ignoresignal(void* args_in) {
1003
0
    ReadyToRunArgs* args = static_cast<ReadyToRunArgs*>(args_in);
1004
0
    TaskGroup* g = BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_task_group);
1005
1006
#ifdef BRPC_BTHREAD_TRACER
1007
    g->_control->_task_tracer.set_status(TASK_STATUS_READY, args->meta);
1008
#endif // BRPC_BTHREAD_TRACER
1009
0
    return g->push_rq(args->meta->tid);
1010
0
}
1011
1012
0
void TaskGroup::priority_to_run(void* args_in) {
1013
0
    ReadyToRunArgs* args = static_cast<ReadyToRunArgs*>(args_in);
1014
0
    TaskGroup* g = BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_task_group);
1015
#ifdef BRPC_BTHREAD_TRACER
1016
    g->_control->_task_tracer.set_status(TASK_STATUS_READY, args->meta);
1017
#endif // BRPC_BTHREAD_TRACER
1018
0
    if (args->meta->priority_index < 0) {
1019
0
        return g->push_rq(args->meta->tid);
1020
0
    }
1021
0
    return g->control()->push_ed_priority_queue(
1022
0
        args->tag, args->meta->priority_index, args->meta->tid);
1023
0
}
1024
1025
struct SleepArgs {
1026
    uint64_t timeout_us;
1027
    bthread_t tid;
1028
    TaskMeta* meta;
1029
    TaskGroup* group;
1030
};
1031
1032
0
static void ready_to_run_from_timer_thread(void* arg) {
1033
0
    CHECK(BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_task_group) == NULL);
1034
0
    const SleepArgs* e = static_cast<const SleepArgs*>(arg);
1035
0
    TaskGroup* g = e->group;
1036
0
    bthread_tag_t tag = g->tag();
1037
0
    g->control()->choose_one_group(tag)->ready_to_run_remote(e->meta);
1038
0
}
1039
1040
0
void TaskGroup::_add_sleep_event(void* void_args) {
1041
    // Must copy SleepArgs. After calling TimerThread::schedule(), previous
1042
    // thread may be stolen by a worker immediately and the on-stack SleepArgs
1043
    // will be gone.
1044
0
    SleepArgs e = *static_cast<SleepArgs*>(void_args);
1045
0
    TaskGroup* g = e.group;
1046
#ifdef BRPC_BTHREAD_TRACER
1047
    g->_control->_task_tracer.set_status(TASK_STATUS_SUSPENDED, e.meta);
1048
#endif // BRPC_BTHREAD_TRACER
1049
1050
0
    TimerThread::TaskId sleep_id;
1051
0
    sleep_id = get_global_timer_thread()->schedule(
1052
0
        ready_to_run_from_timer_thread, void_args,
1053
0
        butil::microseconds_from_now(e.timeout_us));
1054
1055
0
    if (!sleep_id) {
1056
0
        e.meta->sleep_failed = true;
1057
        // Fail to schedule timer, go back to previous thread.
1058
0
        g->ready_to_run(e.meta);
1059
0
        return;
1060
0
    }
1061
1062
    // Set TaskMeta::current_sleep which is for interruption.
1063
0
    const uint32_t given_ver = get_version(e.tid);
1064
0
    {
1065
0
        BAIDU_SCOPED_LOCK(e.meta->version_lock);
1066
0
        if (given_ver == *e.meta->version_butex && !e.meta->interrupted) {
1067
0
            e.meta->current_sleep = sleep_id;
1068
0
            return;
1069
0
        }
1070
0
    }
1071
    // The thread is stopped or interrupted.
1072
    // interrupt() always sees that current_sleep == 0. It will not schedule
1073
    // the calling thread. The race is between current thread and timer thread.
1074
0
    if (get_global_timer_thread()->unschedule(sleep_id) == 0) {
1075
        // added to timer, previous thread may be already woken up by timer and
1076
        // even stopped. It's safe to schedule previous thread when unschedule()
1077
        // returns 0 which means "the not-run-yet sleep_id is removed". If the
1078
        // sleep_id is running(returns 1), ready_to_run_in_worker() will
1079
        // schedule previous thread as well. If sleep_id does not exist,
1080
        // previous thread is scheduled by timer thread before and we don't
1081
        // have to do it again.
1082
0
        g->ready_to_run(e.meta);
1083
0
    }
1084
0
}
1085
1086
// To be consistent with sys_usleep, set errno and return -1 on error.
1087
0
int TaskGroup::usleep(TaskGroup** pg, uint64_t timeout_us) {
1088
0
    if (0 == timeout_us) {
1089
0
        yield(pg);
1090
0
        return 0;
1091
0
    }
1092
0
    TaskGroup* g = *pg;
1093
    // We have to schedule timer after we switched to next bthread otherwise
1094
    // the timer may wake up(jump to) current still-running context.
1095
0
    SleepArgs e = { timeout_us, g->current_tid(), g->current_task(), g };
1096
0
    g->set_remained(_add_sleep_event, &e);
1097
0
    sched(pg);
1098
0
    g = *pg;
1099
0
    if (e.meta->sleep_failed) {
1100
        // Fail to schedule timer, return error.
1101
0
        e.meta->sleep_failed = false;
1102
0
        errno = ESTOP;
1103
0
        return -1;
1104
0
    }
1105
0
    e.meta->current_sleep = 0;
1106
0
    if (e.meta->interrupted) {
1107
        // Race with set and may consume multiple interruptions, which are OK.
1108
0
        e.meta->interrupted = false;
1109
        // NOTE: setting errno to ESTOP is not necessary from bthread's
1110
        // pespective, however many RPC code expects bthread_usleep to set
1111
        // errno to ESTOP when the thread is stopping, and print FATAL
1112
        // otherwise. To make smooth transitions, ESTOP is still set instead
1113
        // of EINTR when the thread is stopping.
1114
0
        errno = (e.meta->stop ? ESTOP : EINTR);
1115
0
        return -1;
1116
0
    }
1117
0
    return 0;
1118
0
}
1119
1120
// Defined in butex.cpp
1121
bool erase_from_butex_because_of_interruption(ButexWaiter* bw);
1122
1123
static int interrupt_and_consume_waiters(
1124
0
    bthread_t tid, ButexWaiter** pw, uint64_t* sleep_id) {
1125
0
    TaskMeta* const m = TaskGroup::address_meta(tid);
1126
0
    if (m == NULL) {
1127
0
        return EINVAL;
1128
0
    }
1129
0
    const uint32_t given_ver = get_version(tid);
1130
0
    BAIDU_SCOPED_LOCK(m->version_lock);
1131
0
    if (given_ver == *m->version_butex) {
1132
0
        *pw = m->current_waiter.exchange(NULL, butil::memory_order_acquire);
1133
0
        *sleep_id = m->current_sleep;
1134
0
        m->current_sleep = 0;  // only one stopper gets the sleep_id
1135
0
        m->interrupted = true;
1136
0
        return 0;
1137
0
    }
1138
0
    return EINVAL;
1139
0
}
1140
1141
0
static int set_butex_waiter(bthread_t tid, ButexWaiter* w) {
1142
0
    TaskMeta* const m = TaskGroup::address_meta(tid);
1143
0
    if (m != NULL) {
1144
0
        const uint32_t given_ver = get_version(tid);
1145
0
        BAIDU_SCOPED_LOCK(m->version_lock);
1146
0
        if (given_ver == *m->version_butex) {
1147
            // Release fence makes m->interrupted visible to butex_wait
1148
0
            m->current_waiter.store(w, butil::memory_order_release);
1149
0
            return 0;
1150
0
        }
1151
0
    }
1152
0
    return EINVAL;
1153
0
}
1154
1155
// The interruption is "persistent" compared to the ones caused by signals,
1156
// namely if a bthread is interrupted when it's not blocked, the interruption
1157
// is still remembered and will be checked at next blocking. This designing
1158
// choice simplifies the implementation and reduces notification loss caused
1159
// by race conditions.
1160
// TODO: bthreads created by BTHREAD_ATTR_PTHREAD blocking on bthread_usleep()
1161
// can't be interrupted.
1162
0
int TaskGroup::interrupt(bthread_t tid, TaskControl* c) {
1163
    // Consume current_waiter in the TaskMeta, wake it up then set it back.
1164
0
    ButexWaiter* w = NULL;
1165
0
    uint64_t sleep_id = 0;
1166
0
    int rc = interrupt_and_consume_waiters(tid, &w, &sleep_id);
1167
0
    if (rc) {
1168
0
        return rc;
1169
0
    }
1170
    // a bthread cannot wait on a butex and be sleepy at the same time.
1171
0
    CHECK(!sleep_id || !w);
1172
0
    if (w != NULL) {
1173
0
        erase_from_butex_because_of_interruption(w);
1174
        // If butex_wait() already wakes up before we set current_waiter back,
1175
        // the function will spin until current_waiter becomes non-NULL.
1176
0
        rc = set_butex_waiter(tid, w);
1177
0
        if (rc) {
1178
0
            LOG(FATAL) << "butex_wait should spin until setting back waiter";
1179
0
            return rc;
1180
0
        }
1181
0
    } else if (sleep_id != 0) {
1182
0
        if (get_global_timer_thread()->unschedule(sleep_id) == 0) {
1183
0
            TaskGroup* g = BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_task_group);
1184
0
            TaskMeta* m = address_meta(tid);
1185
0
            if (g) {
1186
0
                g->ready_to_run(m);
1187
0
            } else {
1188
0
                if (!c) {
1189
0
                    return EINVAL;
1190
0
                }
1191
0
                c->choose_one_group(m->attr.tag)->ready_to_run_remote(m);
1192
0
            }
1193
0
        }
1194
0
    }
1195
0
    return 0;
1196
0
}
1197
1198
0
void TaskGroup::yield(TaskGroup** pg) {
1199
0
    TaskGroup* g = *pg;
1200
0
    ReadyToRunArgs args = { g->tag(), g->_cur_meta, false };
1201
0
    g->set_remained(ready_to_run_in_worker, &args);
1202
0
    sched(pg);
1203
0
}
1204
1205
void print_task(std::ostream& os, bthread_t tid, bool enable_trace,
1206
0
                bool ignore_not_matched = false) {
1207
0
    TaskMeta* const m = TaskGroup::address_meta(tid);
1208
0
    if (m == NULL) {
1209
0
        os << "bthread=" << tid << " : never existed\n";
1210
0
        return;
1211
0
    }
1212
0
    const uint32_t given_ver = get_version(tid);
1213
0
    bool matched = false;
1214
0
    bool stop = false;
1215
0
    bool interrupted = false;
1216
0
    bool about_to_quit = false;
1217
0
    void* (*fn)(void*) = NULL;
1218
0
    void* arg = NULL;
1219
0
    bthread_attr_t attr = BTHREAD_ATTR_NORMAL;
1220
0
    bool has_tls = false;
1221
0
    int64_t cpuwide_start_ns = 0;
1222
0
    TaskStatistics stat = {0, 0, 0};
1223
0
    TaskStatus status = TASK_STATUS_UNKNOWN;
1224
0
    bool traced = false;
1225
0
    pthread_t worker_tid{};
1226
0
    {
1227
0
        BAIDU_SCOPED_LOCK(m->version_lock);
1228
0
        if (given_ver == *m->version_butex) {
1229
0
            matched = true;
1230
0
            stop = m->stop;
1231
0
            interrupted = m->interrupted;
1232
0
            about_to_quit = m->about_to_quit;
1233
0
            fn = m->fn;
1234
0
            arg = m->arg;
1235
0
            attr = m->attr;
1236
0
            has_tls = m->local_storage.keytable;
1237
0
            cpuwide_start_ns = m->cpuwide_start_ns;
1238
0
            stat = m->stat;
1239
0
            status = m->status;
1240
0
            traced = m->traced;
1241
0
            worker_tid = m->worker_tid;
1242
0
        }
1243
0
    }
1244
0
    if (!matched) {
1245
0
        if (!ignore_not_matched) {
1246
0
            os << "bthread=" << tid << " : not exist now\n";
1247
0
        }
1248
0
    } else {
1249
0
        os << "bthread=" << tid << " :\nstop=" << stop
1250
0
           << "\ninterrupted=" << interrupted
1251
0
           << "\nabout_to_quit=" << about_to_quit
1252
0
           << "\nfn=" << (void*)fn
1253
0
           << "\narg=" << (void*)arg
1254
0
           << "\nattr={stack_type=" << attr.stack_type
1255
0
           << " flags=" << attr.flags
1256
0
           << " specified_tag=" << attr.tag
1257
0
           << " name=" << attr.name
1258
0
           << " keytable_pool=" << attr.keytable_pool
1259
0
           << "}\nhas_tls=" << has_tls
1260
0
           << "\nuptime_ns=" << butil::cpuwide_time_ns() - cpuwide_start_ns
1261
0
           << "\ncputime_ns=" << stat.cputime_ns
1262
0
           << "\nnswitch=" << stat.nswitch
1263
#ifdef BRPC_BTHREAD_TRACER
1264
           << "\nstatus=" << status
1265
           << "\ntraced=" << traced
1266
           << "\nworker_tid=" << worker_tid;
1267
        if (enable_trace) {
1268
            os << "\nbthread call stack:\n";
1269
            stack_trace(os, tid);
1270
        }
1271
        os << "\n\n";
1272
 #else
1273
0
           << "\n\n";
1274
0
           (void)status;(void)traced;(void)worker_tid;
1275
0
#endif // BRPC_BTHREAD_TRACER
1276
0
    }
1277
0
}
1278
1279
}  // namespace bthread