Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * shmem_hash.c
4 : : * hash table implementation in shared memory
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : * A shared memory hash table implementation on top of the named, fixed-size
10 : : * shared memory areas managed by shmem.c. Each hash table has its own free
11 : : * list, so hash buckets can be reused when an item is deleted.
12 : : *
13 : : * IDENTIFICATION
14 : : * src/backend/storage/ipc/shmem_hash.c
15 : : *
16 : : *-------------------------------------------------------------------------
17 : : */
18 : :
19 : : #include "postgres.h"
20 : :
21 : : #include "storage/shmem.h"
22 : : #include "storage/shmem_internal.h"
23 : : #include "utils/memutils.h"
24 : :
25 : : /*
26 : : * A very simple allocator used to carve out different parts of a hash table
27 : : * from a previously allocated contiguous shared memory area.
28 : : */
29 : : typedef struct shmem_hash_allocator
30 : : {
31 : : char *next; /* start of free space in the area */
32 : : char *end; /* end of the shmem area */
33 : : } shmem_hash_allocator;
34 : :
35 : : static void *ShmemHashAlloc(Size size, void *alloc_arg);
36 : :
37 : : /*
38 : : * ShmemRequestHash -- Request a shared memory hash table.
39 : : *
40 : : * Similar to ShmemRequestStruct(), but requests a hash table instead of an
41 : : * opaque area.
42 : : */
43 : : void
29 heikki.linnakangas@i 44 :GNC 9963 : ShmemRequestHashWithOpts(const ShmemHashOpts *options)
45 : : {
46 : : ShmemHashOpts *options_copy;
47 : :
48 [ - + ]: 9963 : Assert(options->name != NULL);
49 : :
50 : 9963 : options_copy = MemoryContextAlloc(TopMemoryContext,
51 : : sizeof(ShmemHashOpts));
52 : 9963 : memcpy(options_copy, options, sizeof(ShmemHashOpts));
53 : :
54 : : /* Set options for the fixed-size area holding the hash table */
55 : 9963 : options_copy->base.name = options->name;
56 : 9963 : options_copy->base.size = hash_estimate_size(options_copy->nelems,
57 : : options_copy->hash_info.entrysize);
58 : :
59 : 9963 : ShmemRequestInternal(&options_copy->base, SHMEM_KIND_HASH);
60 : 9963 : }
61 : :
62 : : void
63 : 9939 : shmem_hash_init(void *location, ShmemStructOpts *base_options)
64 : : {
65 : 9939 : ShmemHashOpts *options = (ShmemHashOpts *) base_options;
66 : 9939 : int hash_flags = options->hash_flags;
67 : : HTAB *htab;
68 : :
69 : 9939 : options->hash_info.hctl = location;
70 : 9939 : htab = shmem_hash_create(location, options->base.size, false,
71 : : options->name,
72 : : options->nelems, &options->hash_info, hash_flags);
73 : :
74 [ + - ]: 9939 : if (options->ptr)
75 : 9939 : *options->ptr = htab;
76 : 9939 : }
77 : :
78 : : void
29 heikki.linnakangas@i 79 :UNC 0 : shmem_hash_attach(void *location, ShmemStructOpts *base_options)
80 : : {
81 : 0 : ShmemHashOpts *options = (ShmemHashOpts *) base_options;
82 : 0 : int hash_flags = options->hash_flags;
83 : : HTAB *htab;
84 : :
85 : : /* attach to it rather than allocate and initialize new space */
86 : 0 : hash_flags |= HASH_ATTACH;
87 : 0 : options->hash_info.hctl = location;
88 [ # # ]: 0 : Assert(options->hash_info.hctl != NULL);
89 : 0 : htab = shmem_hash_create(location, options->base.size, true,
90 : : options->name,
91 : : options->nelems, &options->hash_info, hash_flags);
92 : :
93 [ # # ]: 0 : if (options->ptr)
94 : 0 : *options->ptr = htab;
95 : 0 : }
96 : :
97 : : /*
98 : : * ShmemInitHash -- Create and initialize, or attach to, a
99 : : * shared memory hash table.
100 : : *
101 : : * We assume caller is doing some kind of synchronization
102 : : * so that two processes don't try to create/initialize the same
103 : : * table at once. (In practice, all creations are done in the postmaster
104 : : * process; child processes should always be attaching to existing tables.)
105 : : *
106 : : * nelems is the maximum number of hashtable entries.
107 : : *
108 : : * *infoP and hash_flags must specify at least the entry sizes and key
109 : : * comparison semantics (see hash_create()). Flag bits and values specific
110 : : * to shared-memory hash tables are added here, except that callers may
111 : : * choose to specify HASH_PARTITION.
112 : : *
113 : : * Note: This is a legacy interface, kept for backwards compatibility with
114 : : * extensions. Use ShmemRequestHash() in new code!
115 : : */
116 : : HTAB *
117 : 0 : ShmemInitHash(const char *name, /* table string name for shmem index */
118 : : int64 nelems, /* size of the table */
119 : : HASHCTL *infoP, /* info about key and bucket size */
120 : : int hash_flags) /* info about infoP */
121 : : {
122 : : bool found;
123 : : size_t size;
124 : : void *location;
125 : :
126 : 0 : size = hash_estimate_size(nelems, infoP->entrysize);
127 : :
128 : : /*
129 : : * Look it up in the shmem index or allocate.
130 : : *
131 : : * NOTE: The area is requested internally as SHMEM_KIND_STRUCT instead of
132 : : * SHMEM_KIND_HASH. That's correct because we do the hash table
133 : : * initialization by calling shmem_hash_create() ourselves. (We don't
134 : : * expose the request kind to users; if we did, that would be confusing.)
135 : : */
136 : 0 : location = ShmemInitStruct(name, size, &found);
137 : :
138 : 0 : return shmem_hash_create(location, size, found,
139 : : name, nelems, infoP, hash_flags);
140 : : }
141 : :
142 : : /*
143 : : * Initialize or attach to a shared hash table in the given shmem region.
144 : : *
145 : : * This is exposed to allow InitShmemAllocator() to share the logic for
146 : : * bootstrapping the ShmemIndex hash table.
147 : : */
148 : : HTAB *
29 heikki.linnakangas@i 149 :GNC 11180 : shmem_hash_create(void *location, size_t size, bool found,
150 : : const char *name, int64 nelems, HASHCTL *infoP, int hash_flags)
151 : : {
152 : : shmem_hash_allocator allocator;
153 : :
154 : : /*
155 : : * Hash tables allocated in shared memory have a fixed directory and have
156 : : * all elements allocated upfront. We don't support growing because we'd
157 : : * need to grow the underlying shmem region with it.
158 : : *
159 : : * The shared memory allocator must be specified too.
160 : : */
161 : 11180 : infoP->alloc = ShmemHashAlloc;
162 : 11180 : infoP->alloc_arg = NULL;
163 : 11180 : hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_FIXED_SIZE;
164 : :
165 : : /*
166 : : * if it already exists, attach to it rather than allocate and initialize
167 : : * new space
168 : : */
169 [ + - ]: 11180 : if (!found)
170 : : {
171 : 11180 : allocator.next = (char *) location;
172 : 11180 : allocator.end = (char *) location + size;
173 : 11180 : infoP->alloc_arg = &allocator;
174 : : }
175 : : else
176 : : {
177 : : /* Pass location of hashtable header to hash_create */
29 heikki.linnakangas@i 178 :UNC 0 : infoP->hctl = (HASHHDR *) location;
179 : 0 : hash_flags |= HASH_ATTACH;
180 : : }
181 : :
29 heikki.linnakangas@i 182 :GNC 11180 : return hash_create(name, nelems, infoP, hash_flags);
183 : : }
184 : :
185 : : /*
186 : : * ShmemHashAlloc -- alloc callback for shared memory hash tables
187 : : *
188 : : * Carve out the allocation from a pre-allocated region. All shared memory
189 : : * hash tables are initialized with HASH_FIXED_SIZE, so all the allocations
190 : : * happen upfront during initialization and no locking is required.
191 : : */
192 : : static void *
193 : 722028 : ShmemHashAlloc(Size size, void *alloc_arg)
194 : : {
195 : 722028 : shmem_hash_allocator *allocator = (shmem_hash_allocator *) alloc_arg;
196 : : void *result;
197 : :
198 : 722028 : size = MAXALIGN(size);
199 : :
200 [ - + ]: 722028 : if (allocator->end - allocator->next < size)
29 heikki.linnakangas@i 201 :UNC 0 : return NULL;
29 heikki.linnakangas@i 202 :GNC 722028 : result = allocator->next;
203 : 722028 : allocator->next += size;
204 : :
205 : 722028 : return result;
206 : : }
|