Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
302 changes: 302 additions & 0 deletions common/devdoc/unique_thandle_requirements.md
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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

owner

What are these "owners"? Are they... threads? Are they functions? Are they data structures? What are owners?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

owner

So, are you building std::unique_ptr?
We can, but why do we need it?


`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`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handle

probably UNIQUE_HANDLE you want to say here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UNIQUE_THANDLE

so this is the unique_shared_ptr, because THANDLE is shared_ptr.
I hate to curb enthusiasm, but this is likely not needed.
What is the use case for it?

"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."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to agree here. unique_ptr in C++ is great because of RAII. I don't think you get the benefits (automatic resource cleanup) without that language feature and it feels half-baked for C.


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`:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ADOPT

"adopt".


```c
/*item.h*/
typedef struct ITEM_TAG ITEM;

UNIQUE_THANDLE_TYPE_DECLARE(ITEM);

int item_create(int value, UNIQUE_THANDLE(ITEM) item);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ITEM

at this point, ITEM is an incomplete type.

Who creates "item" for me? Is item here some sort of hidden out parameter? Why is it then not with "*"?

@anporumb anporumb Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, it seems in this case that item is an out argument.

Can we please have in C out arguments with "*". Like:

int item_create(int value, UNIQUE_THANDLE(ITEM)* 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*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

list

list is way too complicated as an example here. Do we need it?

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)
Comment thread
parth21999 marked this conversation as resolved.
Resolved
{
int result;

UNIQUE_THANDLE(ITEM) item = UNIQUE_THANDLE_NULL;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UNIQUE_THANDLE(ITEM) item = UNIQUE_THANDLE_NULL;

Line 16 of common/inc/c_pal/unique_thandle_ll.h says The compiler rejects UNIQUE_THANDLE(T) copy = handle;.

Which is it?

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;

Comment thread
parth21999 marked this conversation as resolved.
Resolved
UNIQUE_THANDLE_TYPE_DECLARE(OTHER);

/*item.c*/
void misuses(UNIQUE_THANDLE(ITEM) item, UNIQUE_THANDLE(OTHER) other)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

misuses

you needto add the exact compiler error you're seeing in every case. I am not compiler.

{
UNIQUE_THANDLE(ITEM) temp = UNIQUE_THANDLE_NULL;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UNIQUE_THANDLE_NULL

must be some magic here

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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pointer

pointer is not type UNIQUE_THANDLE(ITEM) so no wonder. why mention it.


/*warnings as errors: moves a handle to another type, OTHER, into a handle to an ITEM*/
UNIQUE_THANDLE_INITIALIZE_MOVE(ITEM)(temp, other);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UNIQUE_THANDLE_INITIALIZE_MOVE

as with move semantics, if I see something in C that changes its argumenets, then I fully expect to pass those arguments by taking their addresses. In thi case:

UNIQUE_THANDLE_INITIALIZE_MOVE(ITEM)(&temp, &other);

}

/*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

@anporumb anporumb Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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 item nullptr even in C++? This hidden NULL-ification is unwanted

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;
}
Comment thread
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. **]**
17 changes: 17 additions & 0 deletions common/inc/c_pal/unique_thandle.h
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*/
58 changes: 58 additions & 0 deletions common/inc/c_pal/unique_thandle_ll.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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A handle is an array of one slot

I absolutely like this idea.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

slot

may I suggest not using "slot", which is 404 in C.

Here's suggestion: array of size 1. Or single element array. Or array with 1 element.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like jmp_buf

implementation detail. We should not know or care how jm_buf is implemented.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

functions that return a handle

But why reject functions that return a UNIQUE_HANDLE?

What wrong with wanting UNIQUE_THANDLE T = lala_create();?

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; \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const struct UNIQUE_THANDLE_OBJECT_TAG(T)* const volatile object;

which one of these const/volatile can be eliminated/moved to the typedef?

} 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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_UNIQUE_INITIALIZE_MOVE

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:

THANDLE(T) t =something_create();

some_api(t);
THANDLE_ASSIGN(T)(&t, NULL);

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*/
2 changes: 2 additions & 0 deletions linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ set(pal_common_h_files
../common/inc/c_pal/thandle_log_context_handle.h
../common/inc/c_pal/tqueue.h
../common/inc/c_pal/tqueue_ll.h
../common/inc/c_pal/unique_thandle.h
../common/inc/c_pal/unique_thandle_ll.h
)

set(pal_common_c_files
Expand Down
2 changes: 2 additions & 0 deletions win32/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ set(pal_common_h_files
../common/inc/c_pal/thandle_log_context_handle.h
../common/inc/c_pal/tqueue.h
../common/inc/c_pal/tqueue_ll.h
../common/inc/c_pal/unique_thandle.h
../common/inc/c_pal/unique_thandle_ll.h
)

set(pal_common_c_files
Expand Down
Loading