-
Notifications
You must be signed in to change notification settings - Fork 7
/
dtable.h
128 lines (117 loc) · 3.73 KB
/
dtable.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include "lock.h"
#include "class.h"
#include "sarray2.h"
#include "objc/slot.h"
#include "visibility.h"
#include <stdint.h>
#include <stdio.h>
#ifdef __OBJC_LOW_MEMORY__
typedef struct objc_dtable* dtable_t;
struct objc_slot* objc_dtable_lookup(dtable_t dtable, uint32_t uid);
#else
typedef SparseArray* dtable_t;
# define objc_dtable_lookup SparseArrayLookup
#endif
/**
* Pointer to the sparse array representing the pretend (uninstalled) dtable.
*/
PRIVATE extern dtable_t uninstalled_dtable;
/**
* Structure for maintaining a linked list of temporary dtables. When sending
* an +initialize message to a class, we create a temporary dtables and store
* it in a linked list. This is then used when sending other messages to
* instances of classes in the middle of initialisation.
*/
typedef struct _InitializingDtable
{
/** The class that owns the dtable. */
Class class;
/** The dtable for this class. */
dtable_t dtable;
/** The next uninstalled dtable in the list. */
struct _InitializingDtable *next;
} InitializingDtable;
/** Head of the list of temporary dtables. Protected by initialize_lock. */
extern InitializingDtable *temporary_dtables;
extern mutex_t initialize_lock;
/**
* Returns whether a class has an installed dtable.
*/
static inline int classHasInstalledDtable(struct objc_class *cls)
{
return (cls->dtable != uninstalled_dtable);
}
int objc_sync_enter(id object);
int objc_sync_exit(id object);
/**
* Returns the dtable for a given class. If we are currently in an +initialize
* method then this will block if called from a thread other than the one
* running the +initialize method.
*/
static inline dtable_t dtable_for_class(Class cls)
{
if (classHasInstalledDtable(cls))
{
return cls->dtable;
}
dtable_t dtable = uninstalled_dtable;
{
LOCK_FOR_SCOPE(&initialize_lock);
if (classHasInstalledDtable(cls))
{
return cls->dtable;
}
/* This is a linear search, and so, in theory, could be very slow. It
* is O(n) where n is the number of +initialize methods on the stack.
* In practice, this is a very small number. Profiling with GNUstep
* showed that this peaks at 8. */
InitializingDtable *buffer = temporary_dtables;
while (NULL != buffer)
{
if (buffer->class == cls)
{
dtable = buffer->dtable;
break;
}
buffer = buffer->next;
}
}
if (dtable != uninstalled_dtable)
{
// Make sure that we block if +initialize is still running. We do this
// after we've released the initialize lock, so that the real dtable
// can be installed. This acquires / releases a recursive mutex, so if
// this mutex is already held by this thread then this will proceed
// immediately. If it's held by another thread (i.e. the one running
// +initialize) then we block here until it's run. We don't need to do
// this if the dtable is the uninstalled dtable, because that means
// +initialize has not yet been sent, so we can wait until something
// triggers it before needing any synchronisation.
objc_sync_enter((id)cls);
objc_sync_exit((id)cls);
}
return dtable;
}
/**
* Returns whether a class has had a dtable created. The dtable may be
* installed, or stored in the look-aside buffer.
*/
static inline int classHasDtable(struct objc_class *cls)
{
return (dtable_for_class(cls) != uninstalled_dtable);
}
/**
* Updates the dtable for a class and its subclasses. Must be called after
* modifying a class's method list.
*/
void objc_update_dtable_for_class(Class);
/**
* Adds a single method list to a class. This is used when loading categories,
* and is faster than completely rebuilding the dtable.
*/
void add_method_list_to_class(Class cls,
struct objc_method_list *list);
/**
* Destroys a dtable.
*/
void free_dtable(dtable_t dtable);