forked from ericsink/SQLitePCL.raw
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhandles.cs
More file actions
378 lines (324 loc) · 11.4 KB
/
Copy pathhandles.cs
File metadata and controls
378 lines (324 loc) · 11.4 KB
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
/*
Copyright 2014-2021 SourceGear, LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright © Microsoft Open Technologies, Inc.
// All Rights Reserved
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
//
// See the Apache 2 License for the specific language governing permissions and limitations under the License.
namespace SQLitePCL
{
using System;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Threading;
public class sqlite3_backup : SafeHandle
{
sqlite3_backup() : base(IntPtr.Zero, true)
{
}
public static sqlite3_backup From(IntPtr p)
{
var h = new sqlite3_backup();
h.SetHandle(p);
return h;
}
public override bool IsInvalid => handle == IntPtr.Zero;
protected override bool ReleaseHandle()
{
int rc = raw.internal_sqlite3_backup_finish(handle);
// TODO check rc?
return true;
}
public int manual_close()
{
int rc = raw.internal_sqlite3_backup_finish(handle);
// TODO review. should handle always be nulled here?
// TODO maybe called SetHandleAsInvalid instead?
handle = IntPtr.Zero;
return rc;
}
}
// typed wrapper for an IntPtr. still opaque. the upper layers can't
// do anything with this except hand it back to us on calls to
// raw.sqlite3_result_*.
//
// except for the 'state' property below, which the upper layers can
// use to store state between calls to
// xStep/xFinal for an aggregate function.
public class sqlite3_context
{
private IntPtr _p;
private object _user_data;
// must be called by one of the two subclass (scalar
// or agg)
protected sqlite3_context(object user_data)
{
_user_data = user_data;
}
// used by raw.sqlite3_user_data (which is internal
// to the PCL assembly)
internal object user_data
{
get
{
return _user_data;
}
}
// used by raw.sqlite3_result_* (which is internal to the
// PCL assembly) to fetch the actual context pointer to pass
// back to sqlite.
internal IntPtr ptr => _p;
// used by either the scalar or agg subclass, located
// in util.cs, compiled into the platform assembly. each
// call to xFunc, xStep, or xFinal actually gives us a
// different context pointer. however, for an aggregate
// function, we want this sqlite3_context object to be the
// same throughout all the calls to xStep or xFinal. so
// we fix the pointer on each call. and we want this to be
// invisible to the upper layers, so make this protected and do
// the fixup in a subclass.
protected void set_context_ptr(IntPtr p)
{
_p = p;
}
// this is available to the upper layers, to store state during
// the run of an aggregate function. not needed for scalar
// functions.
public object state;
}
// typed wrapper for an IntPtr. still opaque. the upper layers can't
// do anything with this except hand it back to us on calls to
// raw.sqlite3_value_*
public class sqlite3_value
{
private IntPtr _p;
public sqlite3_value(IntPtr p)
{
_p = p;
}
internal IntPtr ptr => _p;
}
public class sqlite3_blob : SafeHandle
{
sqlite3_blob() : base(IntPtr.Zero, true)
{
}
internal static sqlite3_blob From(IntPtr p)
{
var h = new sqlite3_blob();
h.SetHandle(p);
return h;
}
public override bool IsInvalid => handle == IntPtr.Zero;
protected override bool ReleaseHandle()
{
int rc = raw.internal_sqlite3_blob_close(handle);
// TODO check rc?
return true;
}
public int manual_close()
{
int rc = raw.internal_sqlite3_blob_close(handle);
// TODO review. should handle always be nulled here?
// TODO maybe called SetHandleAsInvalid instead?
handle = IntPtr.Zero;
return rc;
}
}
public class sqlite3_snapshot : SafeHandle
{
sqlite3_snapshot() : base(IntPtr.Zero, true)
{
}
internal static sqlite3_snapshot From(IntPtr p)
{
var h = new sqlite3_snapshot();
h.SetHandle(p);
return h;
}
public override bool IsInvalid => handle == IntPtr.Zero;
protected override bool ReleaseHandle()
{
raw.internal_sqlite3_snapshot_free(handle);
return true;
}
public void manual_close()
{
raw.internal_sqlite3_snapshot_free(handle);
// TODO review. should handle always be nulled here?
// TODO maybe called SetHandleAsInvalid instead?
handle = IntPtr.Zero;
}
}
public class sqlite3_stmt : SafeHandle
{
private sqlite3 _db;
internal static sqlite3_stmt From(IntPtr p, sqlite3 db)
{
var h = new sqlite3_stmt();
h.SetHandle(p);
db.add_stmt(h);
h._db = db;
return h;
}
sqlite3_stmt() : base(IntPtr.Zero, true)
{
}
public override bool IsInvalid => handle == IntPtr.Zero;
protected override bool ReleaseHandle()
{
int rc = raw.internal_sqlite3_finalize(handle);
// TODO check rc?
_db.remove_stmt(this);
return true;
}
public int manual_close()
{
int rc = raw.internal_sqlite3_finalize(handle);
// TODO review. should handle always be nulled here?
// TODO maybe called SetHandleAsInvalid instead?
handle = IntPtr.Zero;
_db.remove_stmt(this);
return rc;
}
// TODO rm? used by the next_stmt code.
internal IntPtr ptr => handle;
// We keep track of the db connection handle for this stmt, even though
// the underlying sqlite C library keeps track of it as well. On a call
// to sqlite3_db_handle(), if we called the C function and get a pointer
// and then wrap it in a new instance of our sqlite3 class, we would end
// up with two instances of that class having the same wrapped IntPtr.
// This seems bad. So we implement it here at this layer as well.
internal sqlite3 db
{
get
{
return _db;
}
}
}
public class sqlite3 : SafeHandle
{
sqlite3() : base(IntPtr.Zero, true)
{
}
public override bool IsInvalid => handle == IntPtr.Zero;
protected override bool ReleaseHandle()
{
int rc = raw.internal_sqlite3_close_v2(handle);
// TODO check rc?
dispose_extra();
return true;
}
public int manual_close_v2()
{
int rc = raw.internal_sqlite3_close_v2(handle);
// TODO review. should handle always be nulled here?
// TODO maybe called SetHandleAsInvalid instead?
handle = IntPtr.Zero;
dispose_extra();
return rc;
}
public int manual_close()
{
int rc = raw.internal_sqlite3_close(handle);
// TODO review. should handle always be nulled here?
// TODO maybe called SetHandleAsInvalid instead?
handle = IntPtr.Zero;
dispose_extra();
return rc;
}
internal static sqlite3 New(IntPtr p)
{
var h = new sqlite3();
h.SetHandle(p);
#if not // changing this to default OFF for v2
h.enable_sqlite3_next_stmt(true);
#endif
return h;
}
// this dictionary is used only for the purpose of supporting sqlite3_next_stmt.
private ConcurrentDictionary<IntPtr, sqlite3_stmt> _stmts = null;
public void enable_sqlite3_next_stmt(bool enabled)
{
if (enabled)
{
if (_stmts == null)
{
_stmts = new ConcurrentDictionary<IntPtr, sqlite3_stmt>();
}
}
else
{
_stmts = null;
}
}
internal void add_stmt(sqlite3_stmt stmt)
{
if (_stmts != null)
{
_stmts[stmt.ptr] = stmt;
}
}
internal sqlite3_stmt find_stmt(IntPtr p)
{
if (_stmts != null)
{
return _stmts[p];
}
else
{
// any change to the wording of this error message might break a test case
throw new Exception("The sqlite3_next_stmt() function is disabled. To enable it, call sqlite3.enable_sqlite3_next_stmt(true) immediately after opening the sqlite3 connection.");
}
}
internal void remove_stmt(sqlite3_stmt s)
{
if (_stmts != null)
{
_stmts.TryRemove(s.ptr, out var stmt);
}
}
IDisposable extra;
public T GetOrCreateExtra<T>(Func<T> f)
where T : class, IDisposable
{
// Audit #7: atomic check-and-set so two threads racing the first hook registration cannot both create T and silently drop one container's callbacks/GCHandles.
var existing = Volatile.Read(ref extra);
if (existing != null)
{
return (T)existing;
}
var candidate = f();
var previous = Interlocked.CompareExchange(ref extra, candidate, null);
if (previous != null)
{
candidate.Dispose();
return (T)previous;
}
return candidate;
}
private void dispose_extra()
{
// Audit #8: pair Interlocked.Exchange with GetOrCreateExtra's CAS so disposal and creation don't race on a half-disposed object.
var snapshot = Interlocked.Exchange(ref extra, null);
if (snapshot != null)
{
snapshot.Dispose();
}
}
}
}