mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-06 19:41:15 +01:00
Merge branch 'handles_squashed'
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
/* Copyright (c) 2016, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file handles.h
|
||||
* \brief Macros for C weak-handle implementation.
|
||||
*
|
||||
* A 'handle' is a pointer to an object that is allowed to go away while
|
||||
* the handle stays alive. When you dereference the handle, you might get
|
||||
* the object, or you might get "NULL".
|
||||
*
|
||||
* Use this pattern when an object has a single obvious lifespan, so you don't
|
||||
* want to use reference counting, but when other objects might need to refer
|
||||
* to the first object without caring about its lifetime.
|
||||
*
|
||||
* To enable a type to have handles, add a HANDLE_ENTRY() field in its
|
||||
* definition, as in:
|
||||
*
|
||||
* struct walrus {
|
||||
* HANDLE_ENTRY(wlr, walrus);
|
||||
* // ...
|
||||
* };
|
||||
*
|
||||
* And invoke HANDLE_DECL(wlr, walrus, [static]) to declare the handle
|
||||
* manipulation functions (typically in a header):
|
||||
*
|
||||
* // opaque handle to walrus.
|
||||
* typedef struct wlr_handle_t wlr_handle_t;
|
||||
*
|
||||
* // make a new handle
|
||||
* struct wlr_handle_t *wlr_handle_new(struct walrus *);
|
||||
*
|
||||
* // release a handle
|
||||
* void wlr_handle_free(wlr_handle_t *);
|
||||
*
|
||||
* // return the pointed-to walrus, or NULL.
|
||||
* struct walrus *wlr_handle_get(wlr_handle_t *).
|
||||
*
|
||||
* // call this function when you're about to free the walrus;
|
||||
* // it invalidates all handles. (IF YOU DON'T, YOU WILL HAVE
|
||||
* // DANGLING REFERENCES)
|
||||
* void wlr_handles_clear(struct walrus *);
|
||||
*
|
||||
* Finally, use HANDLE_IMPL() to define the above functions in some
|
||||
* appropriate C file: HANDLE_IMPL(wlr, walrus, [static])
|
||||
*
|
||||
**/
|
||||
|
||||
#ifndef TOR_HANDLE_H
|
||||
#define TOR_HANDLE_H
|
||||
|
||||
#include "orconfig.h"
|
||||
#include "tor_queue.h"
|
||||
#include "util.h"
|
||||
|
||||
#define HANDLE_ENTRY(name, structname) \
|
||||
struct name ## _handle_head_t *handle_head
|
||||
|
||||
#define HANDLE_DECL(name, structname, linkage) \
|
||||
typedef struct name ## _handle_t name ## _handle_t; \
|
||||
linkage name ## _handle_t *name ## _handle_new(struct structname *object); \
|
||||
linkage void name ## _handle_free(name ## _handle_t *); \
|
||||
linkage struct structname *name ## _handle_get(name ## _handle_t *); \
|
||||
linkage void name ## _handles_clear(struct structname *object);
|
||||
|
||||
/*
|
||||
* Implementation notes: there are lots of possible implementations here. We
|
||||
* could keep a linked list of handles, each with a backpointer to the object,
|
||||
* and set all of their backpointers to NULL when the object is freed. Or we
|
||||
* could have the clear function invalidate the object, but not actually let
|
||||
* the object get freed until the all the handles went away. We could even
|
||||
* have a hash-table mapping unique identifiers to objects, and have each
|
||||
* handle be a copy of the unique identifier. (We'll want to build that last
|
||||
* one eventually if we want cross-process handles.)
|
||||
*
|
||||
* But instead we're opting for a single independent 'head' that knows how
|
||||
* many handles there are, and where the object is (or isn't). This makes
|
||||
* all of our functions O(1), and most as fast as a single pointer access.
|
||||
*
|
||||
* The handles themselves are opaque structures holding a pointer to the head.
|
||||
* We could instead have each foo_handle_t* be identical to foo_handle_head_t
|
||||
* *, and save some allocations ... but doing so would make handle leaks
|
||||
* harder to debug. As it stands, every handle leak is a memory leak, and
|
||||
* existing memory debugging tools should help with those. We can revisit
|
||||
* this decision if handles are too slow.
|
||||
*/
|
||||
|
||||
#define HANDLE_IMPL(name, structname, linkage) \
|
||||
/* The 'head' object for a handle-accessible type. This object */ \
|
||||
/* persists for as long as the object, or any handles, exist. */ \
|
||||
typedef struct name ## _handle_head_t { \
|
||||
struct structname *object; /* pointed-to object, or NULL */ \
|
||||
unsigned int references; /* number of existing handles */ \
|
||||
} name ## _handle_head_t; \
|
||||
\
|
||||
struct name ## _handle_t { \
|
||||
struct name ## _handle_head_t *head; /* reference to the 'head'. */ \
|
||||
}; \
|
||||
\
|
||||
linkage struct name ## _handle_t * \
|
||||
name ## _handle_new(struct structname *object) \
|
||||
{ \
|
||||
tor_assert(object); \
|
||||
name ## _handle_head_t *head = object->handle_head; \
|
||||
if (PREDICT_UNLIKELY(head == NULL)) { \
|
||||
head = object->handle_head = tor_malloc_zero(sizeof(*head)); \
|
||||
head->object = object; \
|
||||
} \
|
||||
name ## _handle_t *new_ref = tor_malloc_zero(sizeof(*new_ref)); \
|
||||
new_ref->head = head; \
|
||||
++head->references; \
|
||||
return new_ref; \
|
||||
} \
|
||||
\
|
||||
linkage void \
|
||||
name ## _handle_free(struct name ## _handle_t *ref) \
|
||||
{ \
|
||||
if (! ref) return; \
|
||||
name ## _handle_head_t *head = ref->head; \
|
||||
tor_assert(head); \
|
||||
--head->references; \
|
||||
tor_free(ref); \
|
||||
if (head->object == NULL && head->references == 0) { \
|
||||
tor_free(head); \
|
||||
return; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
linkage struct structname * \
|
||||
name ## _handle_get(struct name ## _handle_t *ref) \
|
||||
{ \
|
||||
tor_assert(ref); \
|
||||
name ## _handle_head_t *head = ref->head; \
|
||||
tor_assert(head); \
|
||||
return head->object; \
|
||||
} \
|
||||
\
|
||||
linkage void \
|
||||
name ## _handles_clear(struct structname *object) \
|
||||
{ \
|
||||
tor_assert(object); \
|
||||
name ## _handle_head_t *head = object->handle_head; \
|
||||
if (! head) \
|
||||
return; \
|
||||
object->handle_head = NULL; \
|
||||
head->object = NULL; \
|
||||
if (head->references == 0) { \
|
||||
tor_free(head); \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif /* TOR_HANDLE_H */
|
||||
|
||||
@@ -130,6 +130,7 @@ COMMONHEADERS = \
|
||||
src/common/crypto_pwbox.h \
|
||||
src/common/crypto_s2k.h \
|
||||
src/common/di_ops.h \
|
||||
src/common/handles.h \
|
||||
src/common/memarea.h \
|
||||
src/common/linux_syscalls.inc \
|
||||
src/common/procmon.h \
|
||||
|
||||
@@ -86,6 +86,7 @@ src_test_test_SOURCES = \
|
||||
src/test/test_guardfraction.c \
|
||||
src/test/test_extorport.c \
|
||||
src/test/test_hs.c \
|
||||
src/test/test_handles.c \
|
||||
src/test/test_introduce.c \
|
||||
src/test/test_keypin.c \
|
||||
src/test/test_link_handshake.c \
|
||||
|
||||
@@ -1177,6 +1177,7 @@ extern struct testcase_t util_tests[];
|
||||
extern struct testcase_t util_format_tests[];
|
||||
extern struct testcase_t util_process_tests[];
|
||||
extern struct testcase_t dns_tests[];
|
||||
extern struct testcase_t handle_tests[];
|
||||
|
||||
struct testgroup_t testgroups[] = {
|
||||
{ "", test_array },
|
||||
@@ -1231,6 +1232,7 @@ struct testgroup_t testgroups[] = {
|
||||
{ "util/logging/", logging_tests },
|
||||
{ "util/process/", util_process_tests },
|
||||
{ "util/thread/", thread_tests },
|
||||
{ "util/handle/", handle_tests },
|
||||
{ "dns/", dns_tests },
|
||||
END_OF_GROUPS
|
||||
};
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@
|
||||
{print_ = (I64_PRINTF_TYPE) value_;}, {}, TT_EXIT_TEST_FUNCTION)
|
||||
|
||||
const char *get_fname(const char *name);
|
||||
crypto_pk_t *pk_generate(int idx);
|
||||
struct crypto_pk_t *pk_generate(int idx);
|
||||
|
||||
#define US2_CONCAT_2__(a, b) a ## __ ## b
|
||||
#define US_CONCAT_2__(a, b) a ## _ ## b
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/* Copyright (c) 2016, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#include "orconfig.h"
|
||||
#include "test.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "handles.h"
|
||||
|
||||
typedef struct demo_t {
|
||||
HANDLE_ENTRY(demo, demo_t);
|
||||
int val;
|
||||
} demo_t;
|
||||
|
||||
HANDLE_DECL(demo, demo_t, static);
|
||||
HANDLE_IMPL(demo, demo_t, static);
|
||||
|
||||
static demo_t *
|
||||
demo_new(int val)
|
||||
{
|
||||
demo_t *d = tor_malloc_zero(sizeof(demo_t));
|
||||
d->val = val;
|
||||
return d;
|
||||
}
|
||||
|
||||
static void
|
||||
demo_free(demo_t *d)
|
||||
{
|
||||
if (d == NULL)
|
||||
return;
|
||||
demo_handles_clear(d);
|
||||
tor_free(d);
|
||||
}
|
||||
|
||||
static void
|
||||
test_handle_basic(void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
demo_t *d1 = NULL, *d2 = NULL;
|
||||
demo_handle_t *wr1 = NULL, *wr2 = NULL, *wr3 = NULL, *wr4 = NULL;
|
||||
|
||||
d1 = demo_new(9000);
|
||||
d2 = demo_new(9009);
|
||||
|
||||
wr1 = demo_handle_new(d1);
|
||||
wr2 = demo_handle_new(d1);
|
||||
wr3 = demo_handle_new(d1);
|
||||
wr4 = demo_handle_new(d2);
|
||||
|
||||
tt_assert(wr1);
|
||||
tt_assert(wr2);
|
||||
tt_assert(wr3);
|
||||
tt_assert(wr4);
|
||||
|
||||
tt_ptr_op(demo_handle_get(wr1), OP_EQ, d1);
|
||||
tt_ptr_op(demo_handle_get(wr2), OP_EQ, d1);
|
||||
tt_ptr_op(demo_handle_get(wr3), OP_EQ, d1);
|
||||
tt_ptr_op(demo_handle_get(wr4), OP_EQ, d2);
|
||||
|
||||
demo_handle_free(wr1);
|
||||
wr1 = NULL;
|
||||
tt_ptr_op(demo_handle_get(wr2), OP_EQ, d1);
|
||||
tt_ptr_op(demo_handle_get(wr3), OP_EQ, d1);
|
||||
tt_ptr_op(demo_handle_get(wr4), OP_EQ, d2);
|
||||
|
||||
demo_free(d1);
|
||||
d1 = NULL;
|
||||
tt_ptr_op(demo_handle_get(wr2), OP_EQ, NULL);
|
||||
tt_ptr_op(demo_handle_get(wr3), OP_EQ, NULL);
|
||||
tt_ptr_op(demo_handle_get(wr4), OP_EQ, d2);
|
||||
|
||||
demo_handle_free(wr2);
|
||||
wr2 = NULL;
|
||||
tt_ptr_op(demo_handle_get(wr3), OP_EQ, NULL);
|
||||
tt_ptr_op(demo_handle_get(wr4), OP_EQ, d2);
|
||||
|
||||
demo_handle_free(wr3);
|
||||
wr3 = NULL;
|
||||
done:
|
||||
demo_handle_free(wr1);
|
||||
demo_handle_free(wr2);
|
||||
demo_handle_free(wr3);
|
||||
demo_handle_free(wr4);
|
||||
demo_free(d1);
|
||||
demo_free(d2);
|
||||
}
|
||||
|
||||
#define HANDLE_TEST(name, flags) \
|
||||
{ #name, test_handle_ ##name, (flags), NULL, NULL }
|
||||
|
||||
struct testcase_t handle_tests[] = {
|
||||
HANDLE_TEST(basic, 0),
|
||||
END_OF_TESTCASES
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user