Repository navigation
Specify UNIQUE_THANDLE and declare its API #665
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6416566
ef43ba5
2ca4bec
7993e12
2290351
edd8fb6
be36c30
45713dc
a81095e
eda0f11
8422d36
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,302 @@ | ||
| # unique_thandle_requirements | ||
|
|
||
| ## Overview | ||
|
|
||
| `unique_thandle` is a module that provides macros for handles that have a single owner. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| `UNIQUE_THANDLE(T)` is a handle to an object of type `T` that has exactly one owner at a time. A handle either holds an object or is `NULL`. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so this is the unique_shared_ptr, because THANDLE is shared_ptr. "It makes transfer possible and explicit. It doesn't make frequent transfer good design. Typical healthy uses are short: a factory returns ownership, a container takes it with one move, and everything else borrows. An object passed through many owners usually means nobody really owns its lifetime. That's what geo does: request → batch → executor, a transfer at every hop. Parth's own justification in !17302701 shows why: the move exists partly so "a request that kept its effects can't form a request → effect → request cycle (async effects hold their request)." So the transfers work around a reference cycle that THANDLE created. UNIQUE_THANDLE would add a second primitive to work around the first one. A simpler design would give effects one stable owner (the request or the strand), have the batch and executor borrow them, and not let effects hold their request through a strong ref. Then there's nothing to transfer and no new primitive is needed."
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm going to agree here. |
||
|
|
||
| The .c file that defines `T` creates the object with `UNIQUE_THANDLE_MALLOC` or `UNIQUE_THANDLE_MALLOC_FLEX` and puts it in a handle with `UNIQUE_THANDLE_ADOPT`: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| ```c | ||
| /*item.h*/ | ||
| typedef struct ITEM_TAG ITEM; | ||
|
|
||
| UNIQUE_THANDLE_TYPE_DECLARE(ITEM); | ||
|
|
||
| int item_create(int value, UNIQUE_THANDLE(ITEM) item); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, it seems in this case that Can we please have in C out arguments with " int item_create(int value, UNIQUE_THANDLE(ITEM) Otherwise NOBODY will guess that this is an out argument. |
||
|
|
||
| /*item.c*/ | ||
| struct ITEM_TAG | ||
| { | ||
| int value; | ||
| }; | ||
|
|
||
| UNIQUE_THANDLE_TYPE_DEFINE(ITEM); | ||
|
|
||
| int item_create(int value, UNIQUE_THANDLE(ITEM) item) | ||
| { | ||
| int result; | ||
| ITEM* new_item = UNIQUE_THANDLE_MALLOC(ITEM)(NULL); | ||
| if (new_item == NULL) | ||
| { | ||
| result = MU_FAILURE; | ||
| } | ||
| else | ||
| { | ||
| new_item->value = value; | ||
| UNIQUE_THANDLE_ADOPT(ITEM)(item, new_item); | ||
| result = 0; | ||
| } | ||
| return result; | ||
| } | ||
| ``` | ||
|
|
||
| The object can be moved with `UNIQUE_THANDLE_INITIALIZE_MOVE` or disposed with `UNIQUE_THANDLE_DISPOSE`. | ||
|
|
||
| ```c | ||
| /*list.h*/ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| typedef struct NODE_TAG | ||
| { | ||
| struct NODE_TAG* next; | ||
| UNIQUE_THANDLE(ITEM) item; | ||
| } NODE; | ||
|
|
||
| typedef struct LIST_TAG | ||
| { | ||
| NODE head; /*holds no item*/ | ||
| NODE* end; /*the last node, &head when the list is empty*/ | ||
| } LIST; | ||
|
|
||
| typedef LIST* LIST_HANDLE; | ||
|
|
||
| int list_insert_num(LIST_HANDLE list, int n); | ||
| int list_insert_item(LIST_HANDLE list, UNIQUE_THANDLE(ITEM) item); | ||
|
|
||
| /*list.c*/ | ||
| int list_insert_num(LIST_HANDLE list, int n) | ||
|
parth21999 marked this conversation as resolved.
Resolved
|
||
| { | ||
| int result; | ||
|
|
||
| UNIQUE_THANDLE(ITEM) item = UNIQUE_THANDLE_NULL; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| if (item_create(n, item) != 0) | ||
| { | ||
| result = MU_FAILURE; | ||
| } | ||
| else | ||
| { | ||
| if (list_insert_item(list, item) != 0) | ||
| { | ||
| result = MU_FAILURE; | ||
| } | ||
| else | ||
| { | ||
| result = 0; | ||
| goto all_ok; | ||
| } | ||
| UNIQUE_THANDLE_DISPOSE(ITEM)(item); | ||
| } | ||
| all_ok: | ||
| return result; | ||
| } | ||
|
|
||
| int list_insert_item(LIST_HANDLE list, UNIQUE_THANDLE(ITEM) item) | ||
| { | ||
| int result; | ||
| if (list == NULL || item == NULL) | ||
| { | ||
| result = MU_FAILURE; | ||
| } | ||
| else | ||
| { | ||
| NODE* node = malloc(sizeof(NODE)); | ||
| if (node == NULL) | ||
| { | ||
| result = MU_FAILURE; | ||
| } | ||
| else | ||
| { | ||
| node->next = NULL; | ||
| UNIQUE_THANDLE_INITIALIZE_MOVE(ITEM)(node->item, item); | ||
| list->end->next = node; | ||
| list->end = node; | ||
| result = 0; | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| ``` | ||
|
|
||
| The compiler rejects the following misuses: | ||
|
|
||
| ```c | ||
| /*other.h*/ | ||
| typedef struct OTHER_TAG OTHER; | ||
|
|
||
|
parth21999 marked this conversation as resolved.
Resolved
|
||
| UNIQUE_THANDLE_TYPE_DECLARE(OTHER); | ||
|
|
||
| /*item.c*/ | ||
| void misuses(UNIQUE_THANDLE(ITEM) item, UNIQUE_THANDLE(OTHER) other) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| { | ||
| UNIQUE_THANDLE(ITEM) temp = UNIQUE_THANDLE_NULL; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| ITEM* pointer = UNIQUE_THANDLE_GET_T(ITEM)(item); | ||
|
|
||
| /*copies a handle: copy and item would both own the item*/ | ||
| UNIQUE_THANDLE(ITEM) copy = item; | ||
|
|
||
| /*assigns a handle: temp and item would both own the item*/ | ||
| temp = item; | ||
|
|
||
| /*writes the pointer in a handle: temp and item would both own the item*/ | ||
| temp[0].object = item[0].object; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a bonus |
||
|
|
||
| /*warnings as errors: makes a handle from a pointer to an item, which a handle may already own*/ | ||
| UNIQUE_THANDLE_INITIALIZE_MOVE(ITEM)(temp, &pointer); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| /*warnings as errors: moves a handle to another type, OTHER, into a handle to an ITEM*/ | ||
| UNIQUE_THANDLE_INITIALIZE_MOVE(ITEM)(temp, other); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
|
|
||
| /*list.h*/ | ||
| /*returns a handle: the caller and the list would both own the item*/ | ||
| UNIQUE_THANDLE(ITEM) list_get(LIST_HANDLE list, uint32_t index); | ||
|
|
||
| /*list.c*/ | ||
| /*node_get_value reads a node*/ | ||
| int node_get_value(const NODE* node) | ||
| { | ||
| UNIQUE_THANDLE(ITEM) temp = UNIQUE_THANDLE_NULL; | ||
|
|
||
| /*warnings as errors: moves the item, which the node owns, into temp*/ | ||
| UNIQUE_THANDLE_INITIALIZE_MOVE(ITEM)(temp, node->item); | ||
| ... | ||
| } | ||
| ``` | ||
|
|
||
| Read-only copies should be passed as `const`: | ||
|
|
||
| ```c | ||
| /*item_get_value uses the item during the call*/ | ||
| int item_get_value(const UNIQUE_THANDLE(ITEM) item) | ||
| { | ||
| UNIQUE_THANDLE(ITEM) temp = UNIQUE_THANDLE_NULL; | ||
|
|
||
| UNIQUE_THANDLE_INITIALIZE_MOVE(ITEM)(temp, item); /*warnings as errors: does not compile*/ | ||
|
|
||
| UNIQUE_THANDLE_DISPOSE(ITEM)(item); /*warnings as errors: does not compile*/ | ||
|
|
||
| ITEM* item_ptr = UNIQUE_THANDLE_GET_T(ITEM)(item); | ||
| return item_ptr->value; | ||
| } | ||
| ``` | ||
|
|
||
| If a read-only copy is passed as non-const and the copy is modified, the original is also changed: | ||
|
|
||
| ```c | ||
| int item_get_value(UNIQUE_THANDLE(ITEM) item) | ||
| { | ||
| UNIQUE_THANDLE(ITEM) temp = UNIQUE_THANDLE_NULL; | ||
|
|
||
| UNIQUE_THANDLE_INITIALIZE_MOVE(ITEM)(temp, item); | ||
|
|
||
| ITEM* item_ptr = UNIQUE_THANDLE_GET_T(ITEM)(temp); | ||
|
|
||
| return item_ptr->value; | ||
| } | ||
|
|
||
| int caller(void) | ||
| { | ||
| UNIQUE_THANDLE(ITEM) item = UNIQUE_THANDLE_NULL; | ||
| item_create(42, item); | ||
|
|
||
| int val = item_get_value(item); | ||
|
|
||
| /*item is now NULL: item_get_value moved the item into temp, which leaked it. Another call to item_get_value dereferences NULL and crashes*/ | ||
| int val2 = item_get_value(item); // will crash | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no. just no. there's no indication to the reader of this code that "item" is changed in between lines 202 and 205. I hate to say it, but this is very unidiomatic C. Completely non-canon. Goes against the orthodoxy. /flails hands helplessly What does unique_ptr in C++ in this case? When you pass a unique_ptr to a function as an argument, after the function returns, does it disappear from the caller, or does it stay with the caller? Don't you need like some explicit "move" to explicitly make the Read it as: this syntax is "special" so the user needs to pay a steep price both using it and reviewing it. Probably frustrating to write too. |
||
|
|
||
| return val + val2; | ||
| } | ||
|
parth21999 marked this conversation as resolved.
Resolved
|
||
| ``` | ||
|
|
||
| Only one thread at a time may move or dispose a given `UNIQUE_THANDLE(T)` variable. | ||
|
|
||
| ## Exposed API | ||
|
|
||
| ```c | ||
| /*the type of a handle to a T*/ | ||
| #define UNIQUE_THANDLE(T) | ||
|
|
||
| /*initializes a handle to NULL, as in UNIQUE_THANDLE(T) handle = UNIQUE_THANDLE_NULL;*/ | ||
| #define UNIQUE_THANDLE_NULL | ||
|
|
||
| /*to be used in a header file*/ | ||
| #define UNIQUE_THANDLE_TYPE_DECLARE(T) | ||
|
|
||
| /*to be used in a .c file*/ | ||
| #define UNIQUE_THANDLE_TYPE_DEFINE(T) | ||
| #define UNIQUE_THANDLE_TYPE_DEFINE_WITH_MALLOC_FUNCTIONS(T, malloc_function, malloc_flex_function, free_function) | ||
|
|
||
| /*declared by UNIQUE_THANDLE_TYPE_DECLARE*/ | ||
| MOCKABLE_FUNCTION(, void, UNIQUE_THANDLE_INITIALIZE_MOVE(T), UNIQUE_THANDLE(T), t1, UNIQUE_THANDLE(T), t2); | ||
| MOCKABLE_FUNCTION(, void, UNIQUE_THANDLE_DISPOSE(T), UNIQUE_THANDLE(T), t); | ||
|
|
||
| /*defined by UNIQUE_THANDLE_TYPE_DEFINE for use in the .c file that defines T*/ | ||
| /*allocates a T. dispose, if it is not NULL, is called when the object is disposed*/ | ||
| static T* UNIQUE_THANDLE_MALLOC(T)(void(*dispose)(T*)); | ||
| /*allocates a T with an additional nmemb * size bytes for its flexible array member*/ | ||
| static T* UNIQUE_THANDLE_MALLOC_FLEX(T)(void(*dispose)(T*), size_t nmemb, size_t size); | ||
| /*frees the memory of a T allocated by UNIQUE_THANDLE_MALLOC or UNIQUE_THANDLE_MALLOC_FLEX, for example when creating the object fails*/ | ||
| static void UNIQUE_THANDLE_FREE(T)(T* t); | ||
| /*puts a new object, which UNIQUE_THANDLE_MALLOC or UNIQUE_THANDLE_MALLOC_FLEX returned, in *t, which is NULL or not initialized*/ | ||
| static void UNIQUE_THANDLE_ADOPT(T)(UNIQUE_THANDLE(T) t, T* object); | ||
| /*returns a pointer to the T that *t holds, or NULL if t is NULL*/ | ||
| static T* UNIQUE_THANDLE_GET_T(T)(const UNIQUE_THANDLE(T) t); | ||
| ``` | ||
|
|
||
| `unique_thandle_ll.h` also provides `UNIQUE_THANDLE_MACRO(T)`, `UNIQUE_THANDLE_LL_TYPE_DECLARE(C, T)`, `UNIQUE_THANDLE_LL_TYPE_DEFINE(C, T)` and `UNIQUE_THANDLE_LL_TYPE_DEFINE_WITH_MALLOC_FUNCTIONS(C, T, malloc_function, malloc_flex_function, free_function)`. `UNIQUE_THANDLE_MACRO(T)` introduces `UNIQUE_THANDLE(T)`, once per translation unit. The other three name the functions after `C` and wrap the type `T`. `UNIQUE_THANDLE_TYPE_DECLARE(T)` is `UNIQUE_THANDLE_MACRO(T)` followed by `UNIQUE_THANDLE_LL_TYPE_DECLARE(T, T)`. | ||
|
|
||
| ## UNIQUE_THANDLE_TYPE_DEFINE(T) | ||
|
|
||
| ```c | ||
| #define UNIQUE_THANDLE_TYPE_DEFINE(T) | ||
| ``` | ||
|
|
||
| `UNIQUE_THANDLE_TYPE_DEFINE` defines the functions declared by `UNIQUE_THANDLE_TYPE_DECLARE`, the functions `UNIQUE_THANDLE_MALLOC(T)`, `UNIQUE_THANDLE_MALLOC_FLEX(T)`, `UNIQUE_THANDLE_FREE(T)`, `UNIQUE_THANDLE_ADOPT(T)` and `UNIQUE_THANDLE_GET_T(T)`, and `UNIQUE_THANDLE_INSPECT(T)`, which shows the object of a handle in a debugger. | ||
|
|
||
| **SRS_UNIQUE_THANDLE_43_001: [** `UNIQUE_THANDLE_TYPE_DEFINE` shall use `malloc`, `malloc_flex` and `free` to allocate and free memory. **]** | ||
|
|
||
| ## UNIQUE_THANDLE_TYPE_DEFINE_WITH_MALLOC_FUNCTIONS(T, malloc_function, malloc_flex_function, free_function) | ||
|
|
||
| ```c | ||
| #define UNIQUE_THANDLE_TYPE_DEFINE_WITH_MALLOC_FUNCTIONS(T, malloc_function, malloc_flex_function, free_function) | ||
| ``` | ||
|
|
||
| `UNIQUE_THANDLE_TYPE_DEFINE_WITH_MALLOC_FUNCTIONS` behaves like `UNIQUE_THANDLE_TYPE_DEFINE`, with memory functions specific to `T`. | ||
|
|
||
| **SRS_UNIQUE_THANDLE_43_002: [** `UNIQUE_THANDLE_TYPE_DEFINE_WITH_MALLOC_FUNCTIONS` shall use `malloc_function`, `malloc_flex_function` and `free_function` to allocate and free memory. **]** | ||
|
|
||
| ## UNIQUE_THANDLE_INITIALIZE_MOVE(T) | ||
|
|
||
| ```c | ||
| MOCKABLE_FUNCTION(, void, UNIQUE_THANDLE_INITIALIZE_MOVE(T), UNIQUE_THANDLE(T), t1, UNIQUE_THANDLE(T), t2); | ||
| ``` | ||
|
|
||
| `UNIQUE_THANDLE_INITIALIZE_MOVE` moves `*t2` into `*t1`, where `*t1` is either `NULL` or not initialized, for example a field of a structure that has just been allocated. | ||
|
|
||
| To set a handle that is not initialized to `NULL`, move a `NULL` handle into it. | ||
|
|
||
| **SRS_UNIQUE_THANDLE_43_008: [** If `t1` is `NULL` then `UNIQUE_THANDLE_INITIALIZE_MOVE` shall return. **]** | ||
|
|
||
| **SRS_UNIQUE_THANDLE_43_009: [** If `t2` is `NULL` then `UNIQUE_THANDLE_INITIALIZE_MOVE` shall return. **]** | ||
|
|
||
| **SRS_UNIQUE_THANDLE_43_010: [** If `t1` is equal to `t2` then `UNIQUE_THANDLE_INITIALIZE_MOVE` shall return. **]** | ||
|
|
||
| **SRS_UNIQUE_THANDLE_43_011: [** `UNIQUE_THANDLE_INITIALIZE_MOVE` shall store `*t2` in `*t1` and set `*t2` to `NULL`. **]** | ||
|
|
||
| ## UNIQUE_THANDLE_DISPOSE(T) | ||
|
|
||
| ```c | ||
| MOCKABLE_FUNCTION(, void, UNIQUE_THANDLE_DISPOSE(T), UNIQUE_THANDLE(T), t); | ||
| ``` | ||
|
|
||
| `UNIQUE_THANDLE_DISPOSE` disposes the handle in `*t` and sets `*t` to `NULL`. | ||
|
|
||
| **SRS_UNIQUE_THANDLE_43_012: [** If `t` is `NULL` then `UNIQUE_THANDLE_DISPOSE` shall return. **]** | ||
|
|
||
| **SRS_UNIQUE_THANDLE_43_013: [** If `*t` is `NULL` then `UNIQUE_THANDLE_DISPOSE` shall return. **]** | ||
|
|
||
| **SRS_UNIQUE_THANDLE_43_014: [** `UNIQUE_THANDLE_DISPOSE` shall set `*t` to `NULL`. **]** | ||
|
|
||
| **SRS_UNIQUE_THANDLE_43_015: [** `UNIQUE_THANDLE_DISPOSE` shall then call the `dispose` function passed when the handle was created, if it is not `NULL`. **]** | ||
|
|
||
| **SRS_UNIQUE_THANDLE_43_016: [** `UNIQUE_THANDLE_DISPOSE` shall free the memory of the handle. **]** | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| // Copyright (c) Microsoft. All rights reserved. | ||
| // Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
|
||
| #ifndef UNIQUE_THANDLE_H | ||
| #define UNIQUE_THANDLE_H | ||
|
|
||
| #include "macro_utils/macro_utils.h" | ||
|
|
||
| #include "c_pal/unique_thandle_ll.h" | ||
|
|
||
| /*macro to be used in headers*/ | ||
| /*introduces UNIQUE_THANDLE(T) for a type T and declares the functions that move and dispose it*/ | ||
| #define UNIQUE_THANDLE_TYPE_DECLARE(T) \ | ||
| UNIQUE_THANDLE_MACRO(T) \ | ||
| UNIQUE_THANDLE_LL_TYPE_DECLARE(T, T) | ||
|
|
||
| #endif /*UNIQUE_THANDLE_H*/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| // Copyright (c) Microsoft. All rights reserved. | ||
| // Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
|
||
| #ifndef UNIQUE_THANDLE_LL_H | ||
| #define UNIQUE_THANDLE_LL_H | ||
|
|
||
| #include <stddef.h> | ||
|
|
||
| #include "macro_utils/macro_utils.h" | ||
|
|
||
| #include "c_pal/thandle_ll.h" | ||
|
|
||
| #include "umock_c/umock_c_prod.h" | ||
|
|
||
| /*UNIQUE_THANDLE(T) is a handle to a T that has a single owner. | ||
| A handle is an array of one slot, like jmp_buf. The compiler rejects UNIQUE_THANDLE(T) copy = handle;, assignments of handles and functions that return a handle. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| A UNIQUE_THANDLE(T) parameter points to the caller's handle, and a const UNIQUE_THANDLE(T) parameter only reads it. | ||
| The slot holds a const pointer to a struct that is only declared. UNIQUE_THANDLE_ADOPT, UNIQUE_THANDLE_INITIALIZE_MOVE and UNIQUE_THANDLE_DISPOSE write the slot, and UNIQUE_THANDLE_GET_T converts its pointer to a T*.*/ | ||
| #define UNIQUE_THANDLE(T) MU_C2(T, _UNIQUE_THANDLE) | ||
|
|
||
| /*initializes a UNIQUE_THANDLE(T) to NULL, as in UNIQUE_THANDLE(T) handle = UNIQUE_THANDLE_NULL;*/ | ||
| #define UNIQUE_THANDLE_NULL { { NULL } } | ||
|
|
||
| /*given a previous type T, these are the struct tags of the slot of UNIQUE_THANDLE(T) and of the object the slot points to. The object struct tag is only declared*/ | ||
| #define UNIQUE_THANDLE_SLOT_TAG(T) MU_C2(T, _UNIQUE_SLOT) | ||
| #define UNIQUE_THANDLE_OBJECT_TAG(T) MU_C2(T, _UNIQUE_OBJECT) | ||
|
|
||
| /*introduces UNIQUE_THANDLE(T), once per type T. The names of the types are built from T, so UNIQUE_THANDLE_LL_TYPE_DECLARE(C, T) for any C uses the same UNIQUE_THANDLE(T)*/ | ||
| #define UNIQUE_THANDLE_MACRO(T) \ | ||
| typedef struct UNIQUE_THANDLE_SLOT_TAG(T) \ | ||
| { \ | ||
| const struct UNIQUE_THANDLE_OBJECT_TAG(T)* const volatile object; \ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } UNIQUE_THANDLE(T)[1]; | ||
|
|
||
| /*given a previous type T, UNIQUE_THANDLE_INITIALIZE_MOVE introduces a new name for a function that moves a handle into a handle that is NULL or not initialized*/ | ||
| #define UNIQUE_THANDLE_INITIALIZE_MOVE(C) MU_C2(C, _UNIQUE_INITIALIZE_MOVE) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are closing remarks. Description.txt mentions that this unique_thandle is needed for some geo reasons. Whatever is happening in geo... why is this is the only way to possibly solve it? I think we are not building the right solution here for that problem. But by using UNIQUE_THANDLE the problem/solution will become even cloudier and more muddy and harder to investigate. Let's go to basics. I suspect a THANDLE "as is" might be equally good. Here's example: If this does not work - we have to understand why it doesn't work. |
||
|
|
||
| /*given a previous type T, UNIQUE_THANDLE_DISPOSE introduces a new name for a function that sets a handle to NULL and disposes the object it held*/ | ||
| #define UNIQUE_THANDLE_DISPOSE(C) MU_C2(C, _UNIQUE_DISPOSE) | ||
|
|
||
| /*given a previous type T, these are the names of the static functions that UNIQUE_THANDLE_LL_TYPE_DEFINE defines for the .c file that defines T*/ | ||
| #define UNIQUE_THANDLE_MALLOC(C) THANDLE_MALLOC(C) | ||
| #define UNIQUE_THANDLE_MALLOC_FLEX(C) THANDLE_MALLOC_FLEX(C) | ||
| #define UNIQUE_THANDLE_FREE(C) THANDLE_FREE(C) | ||
| #define UNIQUE_THANDLE_ADOPT(C) MU_C2(C, _UNIQUE_ADOPT) | ||
| #define UNIQUE_THANDLE_GET_T(C) MU_C2(C, _UNIQUE_GET_T) | ||
|
|
||
| /*given a previous type T, UNIQUE_THANDLE_INSPECT introduces a new name for a function that returns the THANDLE_WRAPPER_TYPE_NAME(T) of the object a handle holds - useful in debugging, like THANDLE_INSPECT. The function has no side-effects*/ | ||
| #define UNIQUE_THANDLE_INSPECT(C) MU_C2(C, _UNIQUE_INSPECT) | ||
|
|
||
| /*macro to be used in headers*/ | ||
| /*declares the functions that move and dispose a UNIQUE_THANDLE(T), named after C. UNIQUE_THANDLE_MACRO(T) introduces UNIQUE_THANDLE(T) before*/ | ||
| /*unlike THANDLE_LL_TYPE_DECLARE, it declares only functions: a translation unit defines the slot struct of T once, and REAL_UNIQUE_THANDLE_DECLARE declares the functions of REAL_T for the UNIQUE_THANDLE(T) that UNIQUE_THANDLE_TYPE_DECLARE(T) introduced*/ | ||
| #define UNIQUE_THANDLE_LL_TYPE_DECLARE(C, T) \ | ||
| MOCKABLE_FUNCTION(, void, UNIQUE_THANDLE_INITIALIZE_MOVE(C), UNIQUE_THANDLE(T), t1, UNIQUE_THANDLE(T), t2); \ | ||
| MOCKABLE_FUNCTION(, void, UNIQUE_THANDLE_DISPOSE(C), UNIQUE_THANDLE(T), t); | ||
|
|
||
| #endif /*UNIQUE_THANDLE_LL_H*/ | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are these "owners"? Are they... threads? Are they functions? Are they data structures? What are
owners?