id.h 8.89 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

gejun's avatar
gejun committed
18 19 20 21 22
// bthread - A M:N threading library to make applications more concurrent.

// Author: Ge,Jun (gejun@baidu.com)
// Date: Tue Jul 10 17:40:58 CST 2012

gejun's avatar
gejun committed
23 24
#ifndef BTHREAD_ID_H
#define BTHREAD_ID_H
gejun's avatar
gejun committed
25

26
#include "butil/macros.h"              // BAIDU_SYMBOLSTR
gejun's avatar
gejun committed
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
#include "bthread/types.h"

__BEGIN_DECLS

// ----------------------------------------------------------------------
// Functions to create 64-bit identifiers that can be attached with data
// and locked without ABA issues. All functions can be called from
// multiple threads simultaneously. Notice that bthread_id_t is designed
// for managing a series of non-heavily-contended actions on an object.
// It's slower than mutex and not proper for general synchronizations.
// ----------------------------------------------------------------------

// Create a bthread_id_t and put it into *id. Crash when `id' is NULL.
// id->value will never be zero.
// `on_error' will be called after bthread_id_error() is called.
// -------------------------------------------------------------------------
// ! User must call bthread_id_unlock() or bthread_id_unlock_and_destroy()
// ! inside on_error.
// -------------------------------------------------------------------------
// Returns 0 on success, error code otherwise.
int bthread_id_create(
    bthread_id_t* id, void* data,
gejun's avatar
gejun committed
49
    int (*on_error)(bthread_id_t id, void* data, int error_code));
gejun's avatar
gejun committed
50 51 52 53 54 55 56 57 58 59

// When this function is called successfully, *id, *id+1 ... *id + range - 1
// are mapped to same internal entity. Operations on any of the id work as
// if they're manipulating a same id. `on_error' is called with the id issued
// by corresponding bthread_id_error(). This is designed to let users encode
// versions into identifiers.
// `range' is limited inside [1, 1024].
int bthread_id_create_ranged(
    bthread_id_t* id, void* data,
    int (*on_error)(bthread_id_t id, void* data, int error_code),
gejun's avatar
gejun committed
60
    int range);
gejun's avatar
gejun committed
61 62 63 64

// Wait until `id' being destroyed.
// Waiting on a destroyed bthread_id_t returns immediately.
// Returns 0 on success, error code otherwise.
gejun's avatar
gejun committed
65
int bthread_id_join(bthread_id_t id);
gejun's avatar
gejun committed
66 67 68

// Destroy a created but never-used bthread_id_t.
// Returns 0 on success, EINVAL otherwise.
gejun's avatar
gejun committed
69
int bthread_id_cancel(bthread_id_t id);
gejun's avatar
gejun committed
70 71 72 73 74 75 76 77 78 79

// Issue an error to `id'.
// If `id' is not locked, lock the id and run `on_error' immediately. Otherwise
// `on_error' will be called with the error just before `id' being unlocked.
// If `id' is destroyed, un-called on_error are dropped.
// Returns 0 on success, error code otherwise.
#define bthread_id_error(id, err)                                        \
    bthread_id_error_verbose(id, err, __FILE__ ":" BAIDU_SYMBOLSTR(__LINE__))

int bthread_id_error_verbose(bthread_id_t id, int error_code, 
gejun's avatar
gejun committed
80
                             const char *location);
gejun's avatar
gejun committed
81 82 83 84

// Make other bthread_id_lock/bthread_id_trylock on the id fail, the id must
// already be locked. If the id is unlocked later rather than being destroyed,
// effect of this function is cancelled. This function avoids useless
gejun's avatar
gejun committed
85
// waiting on a bthread_id which will be destroyed soon but still needs to
gejun's avatar
gejun committed
86 87
// be joinable.
// Returns 0 on success, error code otherwise.
gejun's avatar
gejun committed
88
int bthread_id_about_to_destroy(bthread_id_t id);
gejun's avatar
gejun committed
89 90 91 92

// Try to lock `id' (for using the data exclusively)
// On success return 0 and set `pdata' with the `data' parameter to
// bthread_id_create[_ranged], EBUSY on already locked, error code otherwise.
gejun's avatar
gejun committed
93
int bthread_id_trylock(bthread_id_t id, void** pdata);
gejun's avatar
gejun committed
94 95 96 97 98 99 100 101

// Lock `id' (for using the data exclusively). If `id' is locked
// by others, wait until `id' is unlocked or destroyed.
// On success return 0 and set `pdata' with the `data' parameter to
// bthread_id_create[_ranged], error code otherwise.
#define bthread_id_lock(id, pdata)                                      \
    bthread_id_lock_verbose(id, pdata, __FILE__ ":" BAIDU_SYMBOLSTR(__LINE__))
int bthread_id_lock_verbose(bthread_id_t id, void** pdata, 
gejun's avatar
gejun committed
102
                            const char *location);
gejun's avatar
gejun committed
103 104

// Lock `id' (for using the data exclusively) and reset the range. If `id' is 
105
// locked by others, wait until `id' is unlocked or destroyed. if `range' is
gejun's avatar
gejun committed
106 107 108 109 110 111
// smaller than the original range of this id, nothing happens about the range
#define bthread_id_lock_and_reset_range(id, pdata, range)               \
    bthread_id_lock_and_reset_range_verbose(id, pdata, range,           \
                               __FILE__ ":" BAIDU_SYMBOLSTR(__LINE__))
int bthread_id_lock_and_reset_range_verbose(
    bthread_id_t id, void **pdata, 
gejun's avatar
gejun committed
112
    int range, const char *location);
gejun's avatar
gejun committed
113 114 115 116

// Unlock `id'. Must be called after a successful call to bthread_id_trylock()
// or bthread_id_lock().
// Returns 0 on success, error code otherwise.
gejun's avatar
gejun committed
117
int bthread_id_unlock(bthread_id_t id);
gejun's avatar
gejun committed
118 119 120 121 122

// Unlock and destroy `id'. Waiters blocking on bthread_id_join() or
// bthread_id_lock() will wake up. Must be called after a successful call to
// bthread_id_trylock() or bthread_id_lock().
// Returns 0 on success, error code otherwise.
gejun's avatar
gejun committed
123
int bthread_id_unlock_and_destroy(bthread_id_t id);
gejun's avatar
gejun committed
124 125 126 127 128 129 130 131 132 133

// **************************************************************************
// bthread_id_list_xxx functions are NOT thread-safe unless explicitly stated

// Initialize a list for storing bthread_id_t. When an id is destroyed, it will
// be removed from the list automatically.
// The commented parameters are not used anymore and just kept to not break
// compatibility.
int bthread_id_list_init(bthread_id_list_t* list,
                         unsigned /*size*/,
gejun's avatar
gejun committed
134
                         unsigned /*conflict_size*/);
gejun's avatar
gejun committed
135
// Destroy the list.
gejun's avatar
gejun committed
136
void bthread_id_list_destroy(bthread_id_list_t* list);
gejun's avatar
gejun committed
137 138

// Add a bthread_id_t into the list.
gejun's avatar
gejun committed
139
int bthread_id_list_add(bthread_id_list_t* list, bthread_id_t id);
gejun's avatar
gejun committed
140 141 142

// Swap internal fields of two lists.
void bthread_id_list_swap(bthread_id_list_t* dest, 
gejun's avatar
gejun committed
143
                          bthread_id_list_t* src);
gejun's avatar
gejun committed
144 145 146 147 148 149 150 151 152 153 154 155 156 157

// Issue error_code to all bthread_id_t inside `list' and clear `list'.
// Notice that this function iterates all id inside the list and may call
// on_error() of each valid id in-place, in another word, this thread-unsafe
// function is not suitable to be enclosed within a lock directly.
// To make the critical section small, swap the list inside the lock and
// reset the swapped list outside the lock as follows:
//   bthread_id_list_t tmplist;
//   bthread_id_list_init(&tmplist, 0, 0);
//   LOCK;
//   bthread_id_list_swap(&tmplist, &the_list_to_reset);
//   UNLOCK;
//   bthread_id_list_reset(&tmplist, error_code);
//   bthread_id_list_destroy(&tmplist);
gejun's avatar
gejun committed
158
int bthread_id_list_reset(bthread_id_list_t* list, int error_code);
gejun's avatar
gejun committed
159 160
// Following 2 functions wrap above process.
int bthread_id_list_reset_pthreadsafe(
gejun's avatar
gejun committed
161
    bthread_id_list_t* list, int error_code, pthread_mutex_t* mutex);
gejun's avatar
gejun committed
162
int bthread_id_list_reset_bthreadsafe(
gejun's avatar
gejun committed
163
    bthread_id_list_t* list, int error_code, bthread_mutex_t* mutex);
gejun's avatar
gejun committed
164 165 166 167 168 169 170 171 172

__END_DECLS

#if defined(__cplusplus)
// cpp specific API, with an extra `error_text' so that error information
// is more comprehensive
int bthread_id_create2(
    bthread_id_t* id, void* data,
    int (*on_error)(bthread_id_t id, void* data, int error_code,
gejun's avatar
gejun committed
173
                    const std::string& error_text));
gejun's avatar
gejun committed
174 175 176 177
int bthread_id_create2_ranged(
    bthread_id_t* id, void* data,
    int (*on_error)(bthread_id_t id, void* data, int error_code,
                    const std::string& error_text),
gejun's avatar
gejun committed
178
    int range);
gejun's avatar
gejun committed
179 180 181 182
#define bthread_id_error2(id, ec, et)                                   \
    bthread_id_error2_verbose(id, ec, et, __FILE__ ":" BAIDU_SYMBOLSTR(__LINE__))
int bthread_id_error2_verbose(bthread_id_t id, int error_code,
                              const std::string& error_text,
gejun's avatar
gejun committed
183
                              const char *location);
gejun's avatar
gejun committed
184
int bthread_id_list_reset2(bthread_id_list_t* list, int error_code,
gejun's avatar
gejun committed
185
                           const std::string& error_text);
gejun's avatar
gejun committed
186 187
int bthread_id_list_reset2_pthreadsafe(bthread_id_list_t* list, int error_code,
                                       const std::string& error_text,
gejun's avatar
gejun committed
188
                                       pthread_mutex_t* mutex);
gejun's avatar
gejun committed
189 190
int bthread_id_list_reset2_bthreadsafe(bthread_id_list_t* list, int error_code,
                                       const std::string& error_text,
gejun's avatar
gejun committed
191
                                       bthread_mutex_t* mutex);
gejun's avatar
gejun committed
192 193
#endif

gejun's avatar
gejun committed
194
#endif  // BTHREAD_ID_H