1 /*
2
3 Copyright (c) 2018 Martin Sustrik
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom
10 the Software is furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included
13 in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 IN THE SOFTWARE.
22
23 */
24
25 #include <errno.h>
26 #include <stddef.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "cr.h"
33 #include "handle.h"
34 #include "utils.h"
35 #include "ctx.h"
36
37 #define DILL_DISABLE_RAW_NAMES
38 #include "libdillimpl.h"
39
40 struct dill_handle {
41 /* Table of virtual functions. */
42 struct dill_hvfs *vfs;
43 /* Index of the next handle in the linked list of unused handles. -1 means
44 'the end of the list'. -2 means 'handle is in use'. */
45 int next;
46 /* Cache the last call to hquery. */
47 const void *type;
48 void *ptr;
49 };
50
51 #define DILL_CHECKHANDLE(h, err) \
52 if(dill_slow((h) < 0 || (h) >= ctx->nhandles ||\
53 ctx->handles[(h)].next != -2)) {\
54 errno = EBADF; return (err);}\
55 struct dill_handle *hndl = &ctx->handles[(h)];
56
dill_ctx_handle_init(struct dill_ctx_handle * ctx)57 int dill_ctx_handle_init(struct dill_ctx_handle *ctx) {
58 ctx->handles = NULL;
59 ctx->nhandles = 0;
60 ctx->nused = 0;
61 ctx->first = -1;
62 ctx->last = -1;
63 return 0;
64 }
65
dill_ctx_handle_term(struct dill_ctx_handle * ctx)66 void dill_ctx_handle_term(struct dill_ctx_handle *ctx) {
67 free(ctx->handles);
68 }
69
dill_hmake(struct dill_hvfs * vfs)70 int dill_hmake(struct dill_hvfs *vfs) {
71 struct dill_ctx_handle *ctx = &dill_getctx->handle;
72 if(dill_slow(!vfs || !vfs->query || !vfs->close)) {
73 errno = EINVAL; return -1;}
74 /* Returns ECANCELED if shutting down. */
75 int rc = dill_canblock();
76 if(dill_slow(rc < 0)) return -1;
77 /* If there's no space for the new handle, expand the array.
78 Keep at least 8 handles unused so that there's at least some rotation
79 of handle numbers even if operating close to the current limit. */
80 if(dill_slow(ctx->nhandles - ctx->nused <= 8)) {
81 /* Start with 256 handles, double the size when needed. */
82 int sz = ctx->nhandles ? ctx->nhandles * 2 : 256;
83 struct dill_handle *hndls =
84 realloc(ctx->handles, sz * sizeof(struct dill_handle));
85 if(dill_slow(!hndls)) {errno = ENOMEM; return -1;}
86 /* Add newly allocated handles to the list of unused handles. */
87 int i;
88 for(i = ctx->nhandles; i != sz - 1; ++i)
89 hndls[i].next = i + 1;
90 hndls[sz - 1].next = -1;
91 ctx->first = ctx->nhandles;
92 ctx->last = sz - 1;
93 /* Adjust the array. */
94 ctx->handles = hndls;
95 ctx->nhandles = sz;
96 }
97 /* Return first handle from the list of unused handles. */
98 int h = ctx->first;
99 ctx->first = ctx->handles[h].next;
100 if(dill_slow(ctx->first) == -1) ctx->last = -1;
101 ctx->handles[h].vfs = vfs;
102 ctx->handles[h].next = -2;
103 ctx->handles[h].type = NULL;
104 ctx->handles[h].ptr = NULL;
105 ctx->nused++;
106 return h;
107 }
108
dill_hown(int h)109 int dill_hown(int h) {
110 struct dill_ctx_handle *ctx = &dill_getctx->handle;
111 DILL_CHECKHANDLE(h, -1);
112 /* Create a new handle for the same object. */
113 int res = dill_hmake(hndl->vfs);
114 if(dill_slow(res < 0)) {
115 int rc = dill_hclose(h);
116 dill_assert(rc == 0);
117 return -1;
118 }
119 /* In case handle array was reallocated we have to recompute the pointer. */
120 hndl = &ctx->handles[h];
121 /* Return a handle to the shared pool. */
122 hndl->ptr = NULL;
123 hndl->next = -1;
124 if(ctx->first == -1) ctx->first = h;
125 else ctx->handles[ctx->last].next = h;
126 ctx->last = h;
127 ctx->nused--;
128 return res;
129 }
130
dill_hquery(int h,const void * type)131 void *dill_hquery(int h, const void *type) {
132 struct dill_ctx_handle *ctx = &dill_getctx->handle;
133 DILL_CHECKHANDLE(h, NULL);
134 /* Try and use the cached pointer first; otherwise do the expensive virtual
135 call.*/
136 if(dill_fast(hndl->ptr != NULL && hndl->type == type))
137 return hndl->ptr;
138 else {
139 void *ptr = hndl->vfs->query(hndl->vfs, type);
140 if(dill_slow(!ptr)) return NULL;
141 /* Update cache. */
142 hndl->type = type;
143 hndl->ptr = ptr;
144 return ptr;
145 }
146 }
147
dill_hclose(int h)148 int dill_hclose(int h) {
149 struct dill_ctx_handle *ctx = &dill_getctx->handle;
150 DILL_CHECKHANDLE(h, -1);
151 /* This will guarantee that blocking functions cannot be called anywhere
152 inside the context of the close. */
153 int old = dill_no_blocking(1);
154 /* Send the stop signal to the handle. */
155 hndl->vfs->close(hndl->vfs);
156 /* Restore the previous state. */
157 dill_no_blocking(old);
158 /* Mark the cache as invalid. */
159 hndl->ptr = NULL;
160 /* Return a handle to the shared pool. */
161 hndl->next = -1;
162 if(ctx->first == -1) ctx->first = h;
163 else ctx->handles[ctx->last].next = h;
164 ctx->last = h;
165 ctx->nused--;
166 return 0;
167 }
168
169