今日、FRRでの一般的なロックの実装を見ました。もちろん、強力なマクロfrr_with_mutexをそれらの中に残すことはできません。例を挙げて、ゆっくりとベールを持ち上げます。
frr_with_mutex(&m->mtx) {
if (XXX)
break;
/*
*对m的操作
*/
//AWAKEN(m);
}
frr_with_mutexが何であるかを知るために、次のステップはマクロをレイヤーごとに開くことです。次のマクロは退屈かもしれません。詳しく調べたくない場合は、マクロの結果を直接確認できます。
/* mutex auto-lock/unlock */
/* variant 1:
* (for short blocks, multiple mutexes supported)
* break & return can be used for aborting the block
*
* frr_with_mutex(&mtx, &mtx2) {
* if (error)
* break;
* ...
* }
*/
#define _frr_with_mutex(mutex) \
*NAMECTR(_mtx_) __attribute__(( \
unused, cleanup(_frr_mtx_unlock))) = _frr_mtx_lock(mutex), \
/* end */
#define frr_with_mutex(...) \
for (pthread_mutex_t MACRO_REPEAT(_frr_with_mutex, ##__VA_ARGS__) \
*_once = NULL; _once == NULL; _once = (void *)1) \
/* end */
/* variadic macros, use like:
* #define V_0() ...
* #define V_1(x) ...
* #define V(...) MACRO_VARIANT(V, ##__VA_ARGS__)(__VA_ARGS__)
*/
#define _MACRO_VARIANT(A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10, N, ...) N
#define _CONCAT2(a, b) a ## b
#define _CONCAT(a, b) _CONCAT2(a,b)
#define MACRO_VARIANT(NAME, ...) \
_CONCAT(NAME, _MACRO_VARIANT(0, ##__VA_ARGS__, \
_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, _0))
#define NAMECTR(name) _CONCAT(name, __COUNTER__)
/* per-arg repeat macros, use like:
* #define PERARG(n) ...n...
* #define FOO(...) MACRO_REPEAT(PERARG, ##__VA_ARGS__)
*/
#define _MACRO_REPEAT_0(NAME)
#define _MACRO_REPEAT_1(NAME, A1) \
NAME(A1)
#define _MACRO_REPEAT_2(NAME, A1, A2) \
NAME(A1) NAME(A2)
#define _MACRO_REPEAT_3(NAME, A1, A2, A3) \
NAME(A1) NAME(A2) NAME(A3)
#define _MACRO_REPEAT_4(NAME, A1, A2, A3, A4) \
NAME(A1) NAME(A2) NAME(A3) NAME(A4)
#define _MACRO_REPEAT_5(NAME, A1, A2, A3, A4, A5) \
NAME(A1) NAME(A2) NAME(A3) NAME(A4) NAME(A5)
#define _MACRO_REPEAT_6(NAME, A1, A2, A3, A4, A5, A6) \
NAME(A1) NAME(A2) NAME(A3) NAME(A4) NAME(A5) NAME(A6)
#define _MACRO_REPEAT_7(NAME, A1, A2, A3, A4, A5, A6, A7) \
NAME(A1) NAME(A2) NAME(A3) NAME(A4) NAME(A5) NAME(A6) NAME(A7)
#define _MACRO_REPEAT_8(NAME, A1, A2, A3, A4, A5, A6, A7, A8) \
NAME(A1) NAME(A2) NAME(A3) NAME(A4) NAME(A5) NAME(A6) NAME(A7) NAME(A8)
#define MACRO_REPEAT(NAME, ...) \
MACRO_VARIANT(_MACRO_REPEAT, ##__VA_ARGS__)(NAME, ##__VA_ARGS__)
関連する機能は次のとおりです。
static inline pthread_mutex_t *_frr_mtx_lock(pthread_mutex_t *mutex)
{
pthread_mutex_lock(mutex);
return mutex;
}
static inline void _frr_mtx_unlock(pthread_mutex_t **mutex)
{
if (!*mutex)
return;
pthread_mutex_unlock(*mutex);
*mutex = NULL;
}
ここで、__ COUNTER__は0
最初からの カウントであり、その後、各呼び出し は追加します1的计数器。
いくつかの審議の後、私は次のコードを取得しました:
//frr_with_mutex(mutex)即
for (pthread_mutex_t *NAMECTR(_mtx_) /* 1 */
__attribute__((unused, cleanup(_frr_mtx_unlock))) = _frr_mtx_lock(mutex), /* 2 */
*_once = NULL;_once == NULL; _once = (void *)1) /* 3 */
1:NAMECTR(_mtx_)は名前が変わる変数名であり、__ COUNTER__は呼び出されるたびに1ずつ増加するため、このマクロが使用されるたびに、一時ポインター変数名は_mtx_0、_mtx_1、_mtx_2 ...、postscript_mtx_Xになります。
2:コンパイラ属性は__attribute__
、コンパイラに対する特別な識別、検査、または最適化を説明するために使用されます。ここでは、未使用およびクリーンアップのオプションの属性値が使用されます。
- 変数が定義されていて使用されていない場合、一部のコンパイラーは警告を出します。__attribute __((unused))は、この警告を無視するようにコンパイラーに指示できます。
- __attribute __((cleanup(function)))の関数は、変更された変数がそのライフサイクルを離れると、指定された破棄関数関数が自動的に呼び出されることです。これは、最初に_frr_mtx_lock(mutex)を呼び出し、その戻り値mutexを一時ポインター変数_mtx_Xに渡し、次にforループの最後に_frr_mtx_unlockを呼び出してロックを破棄します。
__attribute __((unused、cleanup(_frr_mtx_unlock)))はここ全体であり、変数を変更するためにNAMECTR(_mtx_)変数の後に配置されます。pthread_mutex_t* NAMECTR(_mtx _)= _frr_mtx_lock(mutex)は関数呼び出しです。
1つの詳細に注意してください。一時変数(第1レベルのポインター)がdestroy関数に渡されると、第2レベルのポインターが渡されます。
3:この_onceの定義、判断、および割り当てには実用的な意味はありませんが、ライフサイクルの/ * 2 * /にある一時変数_mtx_Xのブロックを提供します。_mtx_Xがこの場所で生まれてから死ぬとしましょう。ロック呼び出しのため、_mtx_Xは死ぬと自分自身を破壊します。
frr_with_mutexマクロによって実際に制御されるブロックは、ロックのライフサイクル全体であるforループの本体です。これは、whileの代わりにforを選択する賢い方法であり、ロックの影響範囲を自分で制御できると言わなければなりません。
要約すると、これらの複雑なマクロの背後にはあまりにも多くのことがあると言わざるを得ません。最大の目的は、プログラマーが忘れる可能性のある1つのこと、つまりロックのロック解除を自動的に実行することです。同時に、ロックの影響を受けるブロックサイズを自由に制御できます。それを研究してください。
さらに、最初のサンプルコードでAWAKEN(m)について言及します。これは、後で学習するために使用されます。
#define AWAKEN(m) \
do { \
static unsigned char wakebyte = 0x01; \
write(m->io_pipe[1], &wakebyte, 1); \
} while (0);