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