@k13engineering/linux-dmabuf 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/cached-mapper.d.ts +12 -0
- package/dist/lib/cached-mapper.js +74 -0
- package/dist/lib/dmabuf-handle.d.ts +44 -0
- package/dist/lib/dmabuf-handle.js +282 -0
- package/dist/lib/dmabuf-ioctl.d.ts +11 -0
- package/dist/lib/dmabuf-ioctl.js +67 -0
- package/dist/lib/dmabuf-mapping.d.ts +23 -0
- package/dist/lib/dmabuf-mapping.js +141 -0
- package/dist/lib/dmabuf-transaction.d.ts +20 -0
- package/dist/lib/dmabuf-transaction.js +114 -0
- package/dist/lib/index.d.ts +6 -0
- package/dist/lib/index.js +23 -0
- package/dist/lib/linux-interface-impl.d.ts +3 -0
- package/dist/lib/linux-interface-impl.js +44 -0
- package/dist/lib/linux-interface.d.ts +23 -0
- package/dist/lib/linux-interface.js +24 -0
- package/dist/lib/snippets/gc-guard.d.ts +20 -0
- package/dist/lib/snippets/gc-guard.js +65 -0
- package/package.json +9 -3
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TMemoryMappedBuffer } from "@k13engineering/po6-mmap";
|
|
2
|
+
type TRefcountedBufferMapping = Uint8Array & {
|
|
3
|
+
release: () => void;
|
|
4
|
+
};
|
|
5
|
+
declare const createCachedMapper: ({ map, }: {
|
|
6
|
+
map: () => TMemoryMappedBuffer;
|
|
7
|
+
}) => {
|
|
8
|
+
maybeMap: () => TRefcountedBufferMapping;
|
|
9
|
+
mapped: () => boolean;
|
|
10
|
+
close: () => void;
|
|
11
|
+
};
|
|
12
|
+
export { createCachedMapper };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/*import type { TMemoryMappedBuffer } from "@k13engineering/po6-mmap";*/
|
|
2
|
+
|
|
3
|
+
/*type TRefcountedBufferMapping = Uint8Array & {
|
|
4
|
+
release: () => void;
|
|
5
|
+
};*/
|
|
6
|
+
|
|
7
|
+
const createCachedMapper = ({
|
|
8
|
+
map,
|
|
9
|
+
}/*: {
|
|
10
|
+
map: () => TMemoryMappedBuffer;
|
|
11
|
+
}*/) => {
|
|
12
|
+
|
|
13
|
+
let refcount = 0;
|
|
14
|
+
let mappedBuffer/*: TMemoryMappedBuffer | undefined*/ = undefined;
|
|
15
|
+
|
|
16
|
+
const maybeMap = ()/*: TRefcountedBufferMapping*/ => {
|
|
17
|
+
if (mappedBuffer === undefined) {
|
|
18
|
+
mappedBuffer = map();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
refcount += 1;
|
|
22
|
+
let released = false;
|
|
23
|
+
|
|
24
|
+
// eslint-disable-next-line complexity
|
|
25
|
+
const release = () => {
|
|
26
|
+
if (released) {
|
|
27
|
+
throw Error("handle already released");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (refcount <= 0) {
|
|
31
|
+
throw Error("BUG: release called when refcount is <= 0");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (mappedBuffer === undefined) {
|
|
35
|
+
throw Error("BUG: release called but mappedBuffer is undefined");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
refcount -= 1;
|
|
39
|
+
|
|
40
|
+
if (refcount === 0) {
|
|
41
|
+
mappedBuffer.unmap();
|
|
42
|
+
mappedBuffer = undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
released = true;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const bufferView = new Uint8Array(mappedBuffer.buffer, mappedBuffer.byteOffset, mappedBuffer.byteLength);
|
|
49
|
+
const cachedMappedBuffer = bufferView /*as TRefcountedBufferMapping*/;
|
|
50
|
+
|
|
51
|
+
// monkey-patch mappingId and release method
|
|
52
|
+
cachedMappedBuffer.release = release;
|
|
53
|
+
|
|
54
|
+
return cachedMappedBuffer;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const mapped = () => {
|
|
58
|
+
return mappedBuffer !== undefined;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const close = () => {
|
|
62
|
+
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
maybeMap,
|
|
67
|
+
mapped,
|
|
68
|
+
close
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export {
|
|
73
|
+
createCachedMapper
|
|
74
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type TDmabufMapping, type TDmabufMappingAccess } from "./dmabuf-mapping.js";
|
|
2
|
+
import type { TLinuxDmabufInterface } from "./linux-interface.js";
|
|
3
|
+
type TDmabufInfo = {
|
|
4
|
+
inode: number;
|
|
5
|
+
size: number;
|
|
6
|
+
};
|
|
7
|
+
type TDmabufSync = {
|
|
8
|
+
end: () => void;
|
|
9
|
+
};
|
|
10
|
+
type TSyncReadOrWrite = {
|
|
11
|
+
read: true;
|
|
12
|
+
write: false;
|
|
13
|
+
} | {
|
|
14
|
+
read: false;
|
|
15
|
+
write: true;
|
|
16
|
+
} | {
|
|
17
|
+
read: true;
|
|
18
|
+
write: true;
|
|
19
|
+
};
|
|
20
|
+
type TDmabufHandle = {
|
|
21
|
+
exportAndDupAsDmabufFd: () => {
|
|
22
|
+
dmabufFd: number;
|
|
23
|
+
};
|
|
24
|
+
info: () => TDmabufInfo;
|
|
25
|
+
map: (args: {
|
|
26
|
+
iKnowWhatImDoing: boolean;
|
|
27
|
+
access: TDmabufMappingAccess;
|
|
28
|
+
}) => TDmabufMapping;
|
|
29
|
+
sync: (args: {
|
|
30
|
+
iKnowWhatImDoing: boolean;
|
|
31
|
+
} & TSyncReadOrWrite) => TDmabufSync;
|
|
32
|
+
close: () => void;
|
|
33
|
+
};
|
|
34
|
+
declare const createHandleImporter: ({ linuxInterface }: {
|
|
35
|
+
linuxInterface: TLinuxDmabufInterface;
|
|
36
|
+
}) => {
|
|
37
|
+
importAndDupDmabuf: ({ dmabufFd: providedDmabufFd }: {
|
|
38
|
+
dmabufFd: number;
|
|
39
|
+
}) => TDmabufHandle;
|
|
40
|
+
};
|
|
41
|
+
type THandleImporter = ReturnType<typeof createHandleImporter>;
|
|
42
|
+
type TImportAndDupDmabufFunc = THandleImporter["importAndDupDmabuf"];
|
|
43
|
+
export { createHandleImporter };
|
|
44
|
+
export type { TDmabufHandle, TSyncReadOrWrite, TDmabufSync, TImportAndDupDmabufFunc };
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
|
|
2
|
+
import { /*type TMemoryProtectionFlags */} from "@k13engineering/po6-mmap";
|
|
3
|
+
import { createMappingHelper, /*type TDmabufMapping, *//*type TDmabufMappingAccess */} from "./dmabuf-mapping.js";
|
|
4
|
+
import { createDefaultGarbageCollectedWithoutReleaseError, createGarbageCollectionGuard } from "./snippets/gc-guard.js";
|
|
5
|
+
/*import type { TLinuxDmabufInterface } from "./linux-interface.ts";*/
|
|
6
|
+
|
|
7
|
+
/*type TDmabufInfo = {
|
|
8
|
+
inode: number;
|
|
9
|
+
size: number;
|
|
10
|
+
};*/
|
|
11
|
+
|
|
12
|
+
/*type TDmabufSync = {
|
|
13
|
+
end: () => void;
|
|
14
|
+
};*/
|
|
15
|
+
|
|
16
|
+
/*type TSyncReadOrWrite = {
|
|
17
|
+
read: true;
|
|
18
|
+
write: false;
|
|
19
|
+
} | {
|
|
20
|
+
read: false;
|
|
21
|
+
write: true;
|
|
22
|
+
} | {
|
|
23
|
+
read: true;
|
|
24
|
+
write: true;
|
|
25
|
+
};*/
|
|
26
|
+
|
|
27
|
+
/*type TDmabufHandleInfo = {
|
|
28
|
+
handleId: number;
|
|
29
|
+
inode: number;
|
|
30
|
+
size: number;
|
|
31
|
+
};*/
|
|
32
|
+
|
|
33
|
+
/*type TDmabufHandle = {
|
|
34
|
+
exportAndDupAsDmabufFd: () => { dmabufFd: number };
|
|
35
|
+
info: () => TDmabufInfo;
|
|
36
|
+
map: (args: { iKnowWhatImDoing: boolean, access: TDmabufMappingAccess }) => TDmabufMapping;
|
|
37
|
+
sync: (args: { iKnowWhatImDoing: boolean } & TSyncReadOrWrite) => TDmabufSync;
|
|
38
|
+
close: () => void;
|
|
39
|
+
};*/
|
|
40
|
+
|
|
41
|
+
const dmabufGarbageCollectionGuard = createGarbageCollectionGuard({
|
|
42
|
+
createError: ({ info }/*: { info: TDmabufHandleInfo }*/) => {
|
|
43
|
+
return createDefaultGarbageCollectedWithoutReleaseError({
|
|
44
|
+
name: "DmabufMappingGarbageCollectedWithoutReleaseError",
|
|
45
|
+
info: `dma buffer handle with handleId=${info.handleId} inode=${info.inode} size=${info.size}`,
|
|
46
|
+
releaseFunctionName: "release",
|
|
47
|
+
resourcesName: "dma buffer handles",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
let handleIdCounter = 0;
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
const createHandleImporter = ({
|
|
56
|
+
linuxInterface
|
|
57
|
+
}/*: {
|
|
58
|
+
linuxInterface: TLinuxDmabufInterface
|
|
59
|
+
}*/) => {
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
// eslint-disable-next-line max-statements
|
|
63
|
+
const importAndDupDmabuf = ({ dmabufFd: providedDmabufFd }/*: { dmabufFd: number }*/)/*: TDmabufHandle*/ => {
|
|
64
|
+
|
|
65
|
+
const assertIsADmaBuf = ({ dmabufFd }/*: { dmabufFd: number }*/)/*: void*/ => {
|
|
66
|
+
if (dmabufFd < 0) {
|
|
67
|
+
throw Error(`invalid dmabuf fd ${dmabufFd}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
linuxInterface.fstat({ fd: dmabufFd });
|
|
72
|
+
} catch (ex) {
|
|
73
|
+
throw Error(`invalid dmabuf fd ${dmabufFd}, fstat failed`, { cause: ex /*as Error*/ });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
linuxInterface.dmabufIoctlSyncStart({ dmabufFd, read: true, write: false });
|
|
78
|
+
linuxInterface.dmabufIoctlSyncEnd({ dmabufFd, read: true, write: false });
|
|
79
|
+
} catch (ex) {
|
|
80
|
+
throw Error(`fd ${dmabufFd} is not a valid dmabuf fd, ioctl check failed`, { cause: ex /*as Error*/ });
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
assertIsADmaBuf({ dmabufFd: providedDmabufFd });
|
|
85
|
+
const dmabufFd = linuxInterface.dup({ fd: providedDmabufFd });
|
|
86
|
+
|
|
87
|
+
let closed = false;
|
|
88
|
+
|
|
89
|
+
const st = linuxInterface.fstat({ fd: dmabufFd });
|
|
90
|
+
const inode = st.inode;
|
|
91
|
+
|
|
92
|
+
const assertNotClosed = () => {
|
|
93
|
+
if (closed) {
|
|
94
|
+
throw Error(`dmabuf handle already closed`);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const assertFdIsUnchanged = () => {
|
|
99
|
+
// try {
|
|
100
|
+
// assertIsADmaBuf({ dmabufFd });
|
|
101
|
+
// } catch (e) {
|
|
102
|
+
// let message = `internal dmabuf handle ${dmabufFd} (inode ${inode}) is no longer a valid dmabuf fd,`;
|
|
103
|
+
// message += ` probably someone closed a wrong file descriptor`;
|
|
104
|
+
// message += ` - your program is probably in an inconsistent state`;
|
|
105
|
+
// throw Error(message, { cause: e as Error });
|
|
106
|
+
// }
|
|
107
|
+
|
|
108
|
+
const currentSt = linuxInterface.fstat({ fd: dmabufFd });
|
|
109
|
+
if (currentSt.inode !== inode) {
|
|
110
|
+
let message = `internal dmabuf handle ${dmabufFd} (inode ${inode}) fd's inode has changed to ${currentSt.inode},`;
|
|
111
|
+
message += ` probably someone closed a wrong file descriptor and the OS reused the fd for a different file`;
|
|
112
|
+
message += ` - your program is probably in an inconsistent state`;
|
|
113
|
+
throw Error(message);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const exportAndDupAsDmabufFd/*: TDmabufHandle["exportAndDupAsDmabufFd"]*/ = () => {
|
|
118
|
+
assertNotClosed();
|
|
119
|
+
assertFdIsUnchanged();
|
|
120
|
+
|
|
121
|
+
const newFd = linuxInterface.dup({ fd: dmabufFd });
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
dmabufFd: newFd
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const info/*: TDmabufHandle["info"]*/ = () => {
|
|
129
|
+
assertNotClosed();
|
|
130
|
+
assertFdIsUnchanged();
|
|
131
|
+
|
|
132
|
+
const stat = linuxInterface.fstat({ fd: dmabufFd });
|
|
133
|
+
|
|
134
|
+
const dmabufInfo/*: TDmabufInfo*/ = {
|
|
135
|
+
inode: stat.inode,
|
|
136
|
+
size: stat.size
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
return dmabufInfo;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// type TMapping = {
|
|
143
|
+
// buffer: TMemoryMappedBuffer;
|
|
144
|
+
// access: TMapAccess;
|
|
145
|
+
// };
|
|
146
|
+
|
|
147
|
+
const mapAsserted = ({ memoryProtectionFlags }/*: { memoryProtectionFlags: TMemoryProtectionFlags }*/) => {
|
|
148
|
+
const { size } = info();
|
|
149
|
+
|
|
150
|
+
const { errno, buffer } = linuxInterface.mmapFd({
|
|
151
|
+
fd: dmabufFd,
|
|
152
|
+
mappingVisibility: "MAP_SHARED",
|
|
153
|
+
memoryProtectionFlags,
|
|
154
|
+
genericFlags: {},
|
|
155
|
+
offsetInFd: 0,
|
|
156
|
+
length: size
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
if (errno !== undefined) {
|
|
160
|
+
throw Error(`dmabuf mmap failed with errno ${errno}`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return buffer;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const mappingHelper = createMappingHelper({
|
|
167
|
+
mapAsserted
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const map/*: TDmabufHandle["map"]*/ = ({ iKnowWhatImDoing, access }) => {
|
|
171
|
+
if (!iKnowWhatImDoing) {
|
|
172
|
+
let message = `mapping dmabufs is for internal or advanced usage only:`;
|
|
173
|
+
message += ` dmabuf accesses need proper synchronization;`;
|
|
174
|
+
message += ` accessing the mapped memory after close can cause program crashes or data corruption`;
|
|
175
|
+
message += ` if you really want to do this, please set iKnowWhatImDoing to true`;
|
|
176
|
+
|
|
177
|
+
throw Error(message);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
assertNotClosed();
|
|
181
|
+
assertFdIsUnchanged();
|
|
182
|
+
|
|
183
|
+
const mapping = mappingHelper.map({ access });
|
|
184
|
+
|
|
185
|
+
return mapping;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
let syncStarted = false;
|
|
189
|
+
|
|
190
|
+
const sync/*: TDmabufHandle["sync"]*/ = ({ iKnowWhatImDoing, read, write }) => {
|
|
191
|
+
if (!iKnowWhatImDoing) {
|
|
192
|
+
let message = `creating dmabuf sync is for internal or advanced usage only:`;
|
|
193
|
+
message += ` start / end calls must be properly paired;`;
|
|
194
|
+
message += ` if you really want to do this, please set iKnowWhatImDoing to true`;
|
|
195
|
+
|
|
196
|
+
throw Error(message);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
assertNotClosed();
|
|
200
|
+
assertFdIsUnchanged();
|
|
201
|
+
|
|
202
|
+
if (syncStarted) {
|
|
203
|
+
throw Error(`dmabuf sync already started, make sure to call end() before starting a new sync`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
linuxInterface.dmabufIoctlSyncStart({ dmabufFd, read, write });
|
|
207
|
+
syncStarted = true;
|
|
208
|
+
|
|
209
|
+
let stale = false;
|
|
210
|
+
|
|
211
|
+
const end = () => {
|
|
212
|
+
assertNotClosed();
|
|
213
|
+
assertFdIsUnchanged();
|
|
214
|
+
|
|
215
|
+
if (stale) {
|
|
216
|
+
throw Error(`stale sync instance, end() already called`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
linuxInterface.dmabufIoctlSyncEnd({ dmabufFd, read: true, write: true });
|
|
220
|
+
syncStarted = false;
|
|
221
|
+
stale = true;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
end
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const close/*: TDmabufHandle["close"]*/ = () => {
|
|
230
|
+
assertNotClosed();
|
|
231
|
+
assertFdIsUnchanged();
|
|
232
|
+
|
|
233
|
+
if (syncStarted) {
|
|
234
|
+
throw Error(`dmabuf sync started but not ended, please end sync before closing the handle`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
linuxInterface.close({ fd: dmabufFd });
|
|
238
|
+
closed = true;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const handleId = handleIdCounter;
|
|
242
|
+
handleIdCounter += 1;
|
|
243
|
+
|
|
244
|
+
const { release: protectedClose } = dmabufGarbageCollectionGuard.protect({
|
|
245
|
+
release: () => {
|
|
246
|
+
close();
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
info: {
|
|
250
|
+
handleId,
|
|
251
|
+
inode,
|
|
252
|
+
size: info().size
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
exportAndDupAsDmabufFd,
|
|
258
|
+
info,
|
|
259
|
+
map,
|
|
260
|
+
sync,
|
|
261
|
+
close: protectedClose
|
|
262
|
+
};
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
importAndDupDmabuf
|
|
267
|
+
};
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
/*type THandleImporter = ReturnType<typeof createHandleImporter>;*/
|
|
271
|
+
/*type TImportAndDupDmabufFunc = THandleImporter["importAndDupDmabuf"];*/
|
|
272
|
+
|
|
273
|
+
export {
|
|
274
|
+
createHandleImporter
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
/*export type {
|
|
278
|
+
TDmabufHandle,
|
|
279
|
+
TSyncReadOrWrite,
|
|
280
|
+
TDmabufSync,
|
|
281
|
+
TImportAndDupDmabufFunc
|
|
282
|
+
};*/
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
declare const dmabufIoctlSyncStart: ({ dmabufFd, read, write }: {
|
|
2
|
+
dmabufFd: number;
|
|
3
|
+
read: boolean;
|
|
4
|
+
write: boolean;
|
|
5
|
+
}) => void;
|
|
6
|
+
declare const dmabufIoctlSyncEnd: ({ dmabufFd, read, write }: {
|
|
7
|
+
dmabufFd: number;
|
|
8
|
+
read: boolean;
|
|
9
|
+
write: boolean;
|
|
10
|
+
}) => void;
|
|
11
|
+
export { dmabufIoctlSyncStart, dmabufIoctlSyncEnd };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { ioctl } from "@k13engineering/po6-ioctl";
|
|
2
|
+
import nodeOs from "node:os";
|
|
3
|
+
|
|
4
|
+
const endianness = nodeOs.endianness();
|
|
5
|
+
|
|
6
|
+
const flagsToFlagsBuffer = ({ flags }/*: { flags: bigint }*/) => {
|
|
7
|
+
const flagBuffer = Buffer.alloc(8);
|
|
8
|
+
if (endianness === "LE") {
|
|
9
|
+
flagBuffer.writeBigUInt64LE(flags, 0);
|
|
10
|
+
} else {
|
|
11
|
+
flagBuffer.writeBigUInt64BE(flags, 0);
|
|
12
|
+
}
|
|
13
|
+
return flagBuffer;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const DMA_BUF_IOCTL_SYNC = BigInt(0x40086200);
|
|
17
|
+
|
|
18
|
+
const DMA_BUF_SYNC_READ = BigInt(0x1);
|
|
19
|
+
const DMA_BUF_SYNC_WRITE = BigInt(0x2);
|
|
20
|
+
const DMA_BUF_SYNC_START = BigInt(0x00);
|
|
21
|
+
const DMA_BUF_SYNC_END = BigInt(0x4);
|
|
22
|
+
|
|
23
|
+
const dmabufIoctlSync = ({ dmabufFd, flags }/*: { dmabufFd: number, flags: bigint }*/) => {
|
|
24
|
+
|
|
25
|
+
const flagBuffer = flagsToFlagsBuffer({ flags });
|
|
26
|
+
|
|
27
|
+
const { errno } = ioctl({
|
|
28
|
+
fd: dmabufFd,
|
|
29
|
+
request: DMA_BUF_IOCTL_SYNC,
|
|
30
|
+
arg: flagBuffer
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if (errno !== undefined) {
|
|
34
|
+
throw Error(`dmabuf ioctl sync start failed with errno ${errno}`);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const dmabufIoctlSyncStart = ({ dmabufFd, read, write }/*: { dmabufFd: number, read: boolean; write: boolean }*/) => {
|
|
39
|
+
|
|
40
|
+
let flags = DMA_BUF_SYNC_START;
|
|
41
|
+
if (read) {
|
|
42
|
+
flags |= DMA_BUF_SYNC_READ;
|
|
43
|
+
}
|
|
44
|
+
if (write) {
|
|
45
|
+
flags |= DMA_BUF_SYNC_WRITE;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
dmabufIoctlSync({ dmabufFd, flags });
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const dmabufIoctlSyncEnd = ({ dmabufFd, read, write }/*: { dmabufFd: number, read: boolean; write: boolean }*/) => {
|
|
52
|
+
|
|
53
|
+
let flags = DMA_BUF_SYNC_END;
|
|
54
|
+
if (read) {
|
|
55
|
+
flags |= DMA_BUF_SYNC_READ;
|
|
56
|
+
}
|
|
57
|
+
if (write) {
|
|
58
|
+
flags |= DMA_BUF_SYNC_WRITE;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
dmabufIoctlSync({ dmabufFd, flags });
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export {
|
|
65
|
+
dmabufIoctlSyncStart,
|
|
66
|
+
dmabufIoctlSyncEnd
|
|
67
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { TMemoryMappedBuffer } from "@k13engineering/po6-mmap";
|
|
2
|
+
import { type TMemoryProtectionFlags } from "@k13engineering/po6-mmap/dist/lib/convenience-api.js";
|
|
3
|
+
type TDmabufMappingPolicy = "forbidden" | "required" | "optional";
|
|
4
|
+
type TDmabufMappingAccess = {
|
|
5
|
+
read: TDmabufMappingPolicy;
|
|
6
|
+
write: TDmabufMappingPolicy;
|
|
7
|
+
};
|
|
8
|
+
type TDmabufMapping = Uint8Array & {
|
|
9
|
+
mappingId: number;
|
|
10
|
+
release: () => void;
|
|
11
|
+
};
|
|
12
|
+
declare const createMappingHelper: ({ mapAsserted }: {
|
|
13
|
+
mapAsserted: (args: {
|
|
14
|
+
memoryProtectionFlags: TMemoryProtectionFlags;
|
|
15
|
+
}) => TMemoryMappedBuffer;
|
|
16
|
+
}) => {
|
|
17
|
+
map: ({ access }: {
|
|
18
|
+
access: TDmabufMappingAccess;
|
|
19
|
+
}) => TDmabufMapping;
|
|
20
|
+
close: () => void;
|
|
21
|
+
};
|
|
22
|
+
export { createMappingHelper, };
|
|
23
|
+
export type { TDmabufMapping, TDmabufMappingAccess, TDmabufMappingPolicy };
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/*import type { TMemoryMappedBuffer } from "@k13engineering/po6-mmap";*/
|
|
2
|
+
import { /*type TMemoryProtectionFlags */} from "@k13engineering/po6-mmap/dist/lib/convenience-api.js";
|
|
3
|
+
import { createDefaultGarbageCollectedWithoutReleaseError, createGarbageCollectionGuard } from "./snippets/gc-guard.js";
|
|
4
|
+
import { createCachedMapper } from "./cached-mapper.js";
|
|
5
|
+
|
|
6
|
+
/*type TDmabufMappingPolicy = "forbidden" | "required" | "optional";*/
|
|
7
|
+
|
|
8
|
+
/*type TDmabufMappingAccess = {
|
|
9
|
+
read: TDmabufMappingPolicy;
|
|
10
|
+
write: TDmabufMappingPolicy;
|
|
11
|
+
};*/
|
|
12
|
+
|
|
13
|
+
/*type TDmabufMapping = Uint8Array & {
|
|
14
|
+
mappingId: number;
|
|
15
|
+
release: () => void;
|
|
16
|
+
};*/
|
|
17
|
+
|
|
18
|
+
/*type TDmabufMappingInfo = {
|
|
19
|
+
mappingId: number;
|
|
20
|
+
};*/
|
|
21
|
+
|
|
22
|
+
const createMappingHelper = ({
|
|
23
|
+
mapAsserted
|
|
24
|
+
}/*: {
|
|
25
|
+
mapAsserted: (args: { memoryProtectionFlags: TMemoryProtectionFlags }) => TMemoryMappedBuffer;
|
|
26
|
+
}*/) => {
|
|
27
|
+
|
|
28
|
+
const readOnlyCachedMapper = createCachedMapper({
|
|
29
|
+
map: () => {
|
|
30
|
+
return mapAsserted({
|
|
31
|
+
memoryProtectionFlags: {
|
|
32
|
+
PROT_READ: true,
|
|
33
|
+
PROT_WRITE: false,
|
|
34
|
+
PROT_EXEC: false
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const writeOnlyCachedMapper = createCachedMapper({
|
|
41
|
+
map: () => {
|
|
42
|
+
return mapAsserted({
|
|
43
|
+
memoryProtectionFlags: {
|
|
44
|
+
PROT_READ: false,
|
|
45
|
+
PROT_WRITE: true,
|
|
46
|
+
PROT_EXEC: false
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const readWriteCachedMapper = createCachedMapper({
|
|
53
|
+
map: () => {
|
|
54
|
+
return mapAsserted({
|
|
55
|
+
memoryProtectionFlags: {
|
|
56
|
+
PROT_READ: true,
|
|
57
|
+
PROT_WRITE: true,
|
|
58
|
+
PROT_EXEC: false
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const dmabufMappingGarbageCollectionGuard = createGarbageCollectionGuard({
|
|
65
|
+
createError: ({ info }/*: { info: TDmabufMappingInfo }*/) => {
|
|
66
|
+
return createDefaultGarbageCollectedWithoutReleaseError({
|
|
67
|
+
info: `dma buffer mapping with mappingId=${info.mappingId}`,
|
|
68
|
+
name: "DmabufMappingGarbageCollectedWithoutReleaseError",
|
|
69
|
+
releaseFunctionName: "release",
|
|
70
|
+
resourcesName: "dma buffer mappings",
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
let mappingIdCounter = 0;
|
|
76
|
+
|
|
77
|
+
// eslint-disable-next-line max-statements,complexity
|
|
78
|
+
const map = ({ access }/*: { access: TDmabufMappingAccess }*/)/*: TDmabufMapping*/ => {
|
|
79
|
+
|
|
80
|
+
const mappingId = mappingIdCounter;
|
|
81
|
+
mappingIdCounter += 1;
|
|
82
|
+
// naive approach for now
|
|
83
|
+
|
|
84
|
+
let mapperToUse/*: typeof readOnlyCachedMapper*/;
|
|
85
|
+
|
|
86
|
+
if (access.read === "forbidden" && access.write === "forbidden") {
|
|
87
|
+
throw Error(`invalid mapping access: read and write cannot both be forbidden`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (access.read === "required" && access.write === "required") {
|
|
91
|
+
mapperToUse = readWriteCachedMapper;
|
|
92
|
+
} else if (access.write === "required" || access.read === "forbidden") {
|
|
93
|
+
mapperToUse = writeOnlyCachedMapper;
|
|
94
|
+
} else {
|
|
95
|
+
mapperToUse = readOnlyCachedMapper;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const mappedBuffer = mapperToUse.maybeMap();
|
|
99
|
+
|
|
100
|
+
const mappingInfo/*: TDmabufMappingInfo*/ = {
|
|
101
|
+
mappingId
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const { release } = dmabufMappingGarbageCollectionGuard.protect({
|
|
105
|
+
release: () => {
|
|
106
|
+
mappedBuffer.release();
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
info: mappingInfo
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const bufferView = new Uint8Array(mappedBuffer.buffer, mappedBuffer.byteOffset, mappedBuffer.byteLength);
|
|
113
|
+
const mapping = bufferView /*as TDmabufMapping*/;
|
|
114
|
+
|
|
115
|
+
mapping.mappingId = mappingId;
|
|
116
|
+
mapping.release = release;
|
|
117
|
+
|
|
118
|
+
return mapping;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const close = () => {
|
|
122
|
+
readOnlyCachedMapper.close();
|
|
123
|
+
writeOnlyCachedMapper.close();
|
|
124
|
+
readWriteCachedMapper.close();
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
map,
|
|
129
|
+
close
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export {
|
|
134
|
+
createMappingHelper,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/*export type {
|
|
138
|
+
TDmabufMapping,
|
|
139
|
+
TDmabufMappingAccess,
|
|
140
|
+
TDmabufMappingPolicy
|
|
141
|
+
};*/
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TDmabufHandle } from "./dmabuf-handle.js";
|
|
2
|
+
type TTransactionFunctionArgs = {
|
|
3
|
+
use: (args: {
|
|
4
|
+
handle: TDmabufHandle;
|
|
5
|
+
access: TDmabufUseAccess;
|
|
6
|
+
}) => Uint8Array;
|
|
7
|
+
};
|
|
8
|
+
type TTransactionFunction<T> = (args: TTransactionFunctionArgs) => T;
|
|
9
|
+
type TDmabufUseAccess = {
|
|
10
|
+
read: "required";
|
|
11
|
+
write: "required";
|
|
12
|
+
} | {
|
|
13
|
+
read: "required";
|
|
14
|
+
write: "crash-process-on-write";
|
|
15
|
+
} | {
|
|
16
|
+
read: "crash-process-on-read";
|
|
17
|
+
write: "required";
|
|
18
|
+
};
|
|
19
|
+
declare const dmabufTransaction: <T>(fn: TTransactionFunction<T>) => T;
|
|
20
|
+
export { dmabufTransaction };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/*import type { TDmabufMapping } from "./dmabuf-mapping.ts";*/
|
|
2
|
+
/*import type { TDmabufHandle, TDmabufSync } from "./dmabuf-handle.ts";*/
|
|
3
|
+
|
|
4
|
+
/*type TTransactionFunctionArgs = {
|
|
5
|
+
use: (args: {
|
|
6
|
+
handle: TDmabufHandle;
|
|
7
|
+
access: TDmabufUseAccess
|
|
8
|
+
}) => Uint8Array;
|
|
9
|
+
};*/
|
|
10
|
+
|
|
11
|
+
/*type TTransactionFunction<T> = (args: TTransactionFunctionArgs) => T;*/
|
|
12
|
+
|
|
13
|
+
/*type TDmabufUseAccess = {
|
|
14
|
+
read: "required",
|
|
15
|
+
write: "required"
|
|
16
|
+
} | {
|
|
17
|
+
read: "required",
|
|
18
|
+
write: "crash-process-on-write"
|
|
19
|
+
} | {
|
|
20
|
+
read: "crash-process-on-read",
|
|
21
|
+
write: "required"
|
|
22
|
+
};*/
|
|
23
|
+
|
|
24
|
+
// type TDmabufReadUseAccess = TDmabufUseAccess["read"];
|
|
25
|
+
// type TDmabufWriteUseAccess = TDmabufUseAccess["write"];
|
|
26
|
+
|
|
27
|
+
/*type TUseInternal = {
|
|
28
|
+
handle: TDmabufHandle;
|
|
29
|
+
sync: TDmabufSync;
|
|
30
|
+
mapping: TDmabufMapping;
|
|
31
|
+
};*/
|
|
32
|
+
|
|
33
|
+
let transactionEntered = false;
|
|
34
|
+
|
|
35
|
+
const dmabufTransaction = /*<T>*/(fn/*: TTransactionFunction<T>*/) => {
|
|
36
|
+
|
|
37
|
+
if (transactionEntered) {
|
|
38
|
+
throw Error("nested transactions are not allowed");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let internalUses/*: TUseInternal[]*/ = [];
|
|
42
|
+
|
|
43
|
+
const assertHandleNotAlreadyUsed = ({ handle }/*: { handle: TDmabufHandle }*/) => {
|
|
44
|
+
const existingUses = internalUses.filter((internalUse) => {
|
|
45
|
+
return internalUse.handle === handle;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (existingUses.length > 0) {
|
|
49
|
+
throw Error(`dmabuf handle (inode ${handle.info().inode}) is already used in this transaction`);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const use = ({
|
|
54
|
+
handle,
|
|
55
|
+
access,
|
|
56
|
+
}/*: {
|
|
57
|
+
handle: TDmabufHandle;
|
|
58
|
+
access: TDmabufUseAccess
|
|
59
|
+
}*/) => {
|
|
60
|
+
assertHandleNotAlreadyUsed({ handle });
|
|
61
|
+
|
|
62
|
+
const mapping = handle.map({
|
|
63
|
+
iKnowWhatImDoing: true,
|
|
64
|
+
access: {
|
|
65
|
+
read: access.read === "required" ? "required" : "forbidden",
|
|
66
|
+
write: access.write === "required" ? "required" : "forbidden"
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// @ts-expect-error 2345 - TypeScript does not get this right
|
|
71
|
+
const sync = handle.sync({
|
|
72
|
+
iKnowWhatImDoing: true,
|
|
73
|
+
read: access.read === "required",
|
|
74
|
+
write: access.write === "required"
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const internalHandle/*: TUseInternal*/ = {
|
|
78
|
+
handle,
|
|
79
|
+
sync,
|
|
80
|
+
mapping
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
internalUses = [
|
|
84
|
+
...internalUses,
|
|
85
|
+
internalHandle
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
const buffer = new Uint8Array(mapping.buffer, mapping.byteOffset, mapping.byteLength);
|
|
89
|
+
return buffer;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
transactionEntered = true;
|
|
93
|
+
|
|
94
|
+
let result/*: T*/;
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
result = fn({
|
|
98
|
+
use
|
|
99
|
+
});
|
|
100
|
+
} finally {
|
|
101
|
+
transactionEntered = false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
internalUses.forEach(({ sync, mapping }) => {
|
|
105
|
+
sync.end();
|
|
106
|
+
mapping.release();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return result;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
dmabufTransaction
|
|
114
|
+
};
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { dmabufTransaction } from "./dmabuf-transaction.js";
|
|
2
|
+
import type { TDmabufHandle, TImportAndDupDmabufFunc } from "./dmabuf-handle.js";
|
|
3
|
+
import type { TDmabufMapping } from "./dmabuf-mapping.js";
|
|
4
|
+
declare const importAndDupDmabuf: TImportAndDupDmabufFunc;
|
|
5
|
+
export { dmabufTransaction, importAndDupDmabuf };
|
|
6
|
+
export type { TDmabufHandle, TDmabufMapping, };
|
package/dist/lib/index.js
CHANGED
|
@@ -1 +1,24 @@
|
|
|
1
1
|
|
|
2
|
+
import { dmabufTransaction } from "./dmabuf-transaction.js";
|
|
3
|
+
import { createHandleImporter } from "./dmabuf-handle.js";
|
|
4
|
+
/*import type { TDmabufHandle, TImportAndDupDmabufFunc } from "./dmabuf-handle.ts";*/
|
|
5
|
+
/*import type { TDmabufMapping } from "./dmabuf-mapping.ts";*/
|
|
6
|
+
import { createLinuxHostInterface } from "./linux-interface-impl.js";
|
|
7
|
+
|
|
8
|
+
const linuxInterface = createLinuxHostInterface();
|
|
9
|
+
|
|
10
|
+
const handleImporter = createHandleImporter({
|
|
11
|
+
linuxInterface
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const importAndDupDmabuf = handleImporter.importAndDupDmabuf /*as TImportAndDupDmabufFunc*/;
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
dmabufTransaction,
|
|
18
|
+
importAndDupDmabuf
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/*export type {
|
|
22
|
+
TDmabufHandle,
|
|
23
|
+
TDmabufMapping,
|
|
24
|
+
};*/
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/*import type { TLinuxDmabufInterface } from "./linux-interface.ts";*/
|
|
2
|
+
import { mmapFd } from "@k13engineering/po6-mmap";
|
|
3
|
+
import { dmabufIoctlSyncStart, dmabufIoctlSyncEnd } from "./dmabuf-ioctl.js";
|
|
4
|
+
import { syscall, syscallNumbers } from "syscall-napi";
|
|
5
|
+
import nodeFs from "node:fs";
|
|
6
|
+
|
|
7
|
+
const dup = ({ fd }/*: { fd: number }*/)/*: number*/ => {
|
|
8
|
+
const { errno, ret: newFd } = syscall({
|
|
9
|
+
syscallNumber: syscallNumbers.dup,
|
|
10
|
+
args: [
|
|
11
|
+
BigInt(fd)
|
|
12
|
+
]
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
if (errno !== undefined) {
|
|
16
|
+
throw Error(`dup failed with errno ${errno}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return Number(newFd);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const fstat = ({ fd }/*: { fd: number }*/) => {
|
|
23
|
+
const st = nodeFs.fstatSync(fd);
|
|
24
|
+
return { inode: st.ino, size: st.size };
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const close = ({ fd }/*: { fd: number }*/) => {
|
|
28
|
+
nodeFs.closeSync(fd);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const createLinuxHostInterface = ()/*: TLinuxDmabufInterface*/ => {
|
|
32
|
+
return {
|
|
33
|
+
mmapFd,
|
|
34
|
+
dmabufIoctlSyncEnd,
|
|
35
|
+
dmabufIoctlSyncStart,
|
|
36
|
+
dup,
|
|
37
|
+
fstat,
|
|
38
|
+
close
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export {
|
|
43
|
+
createLinuxHostInterface
|
|
44
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { dmabufIoctlSyncEnd, dmabufIoctlSyncStart } from "./dmabuf-ioctl.js";
|
|
2
|
+
import type { mmapFd } from "@k13engineering/po6-mmap";
|
|
3
|
+
type TDmabufIoctlSyncEndFunc = typeof dmabufIoctlSyncEnd;
|
|
4
|
+
type TDmabufIoctlSyncStartFunc = typeof dmabufIoctlSyncStart;
|
|
5
|
+
type TMmapFdFunc = typeof mmapFd;
|
|
6
|
+
type TLinuxDmabufInterface = {
|
|
7
|
+
mmapFd: TMmapFdFunc;
|
|
8
|
+
dmabufIoctlSyncEnd: TDmabufIoctlSyncEndFunc;
|
|
9
|
+
dmabufIoctlSyncStart: TDmabufIoctlSyncStartFunc;
|
|
10
|
+
dup: (args: {
|
|
11
|
+
fd: number;
|
|
12
|
+
}) => number;
|
|
13
|
+
fstat: (args: {
|
|
14
|
+
fd: number;
|
|
15
|
+
}) => {
|
|
16
|
+
inode: number;
|
|
17
|
+
size: number;
|
|
18
|
+
};
|
|
19
|
+
close: (args: {
|
|
20
|
+
fd: number;
|
|
21
|
+
}) => void;
|
|
22
|
+
};
|
|
23
|
+
export type { TLinuxDmabufInterface };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/* c8 ignore start */
|
|
2
|
+
/*import type {
|
|
3
|
+
dmabufIoctlSyncEnd,
|
|
4
|
+
dmabufIoctlSyncStart,
|
|
5
|
+
} from "./dmabuf-ioctl.ts";*/
|
|
6
|
+
/*import type { mmapFd } from "@k13engineering/po6-mmap";*/
|
|
7
|
+
|
|
8
|
+
/*type TDmabufIoctlSyncEndFunc = typeof dmabufIoctlSyncEnd;*/
|
|
9
|
+
/*type TDmabufIoctlSyncStartFunc = typeof dmabufIoctlSyncStart;*/
|
|
10
|
+
/*type TMmapFdFunc = typeof mmapFd;*/
|
|
11
|
+
|
|
12
|
+
/*type TLinuxDmabufInterface = {
|
|
13
|
+
mmapFd: TMmapFdFunc;
|
|
14
|
+
dmabufIoctlSyncEnd: TDmabufIoctlSyncEndFunc;
|
|
15
|
+
dmabufIoctlSyncStart: TDmabufIoctlSyncStartFunc;
|
|
16
|
+
dup: (args: { fd: number }) => number;
|
|
17
|
+
fstat: (args: { fd: number }) => { inode: number; size: number };
|
|
18
|
+
close: (args: { fd: number }) => void;
|
|
19
|
+
};*/
|
|
20
|
+
|
|
21
|
+
/*export type {
|
|
22
|
+
TLinuxDmabufInterface
|
|
23
|
+
};*/
|
|
24
|
+
/* c8 ignore end */
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type TInstance = {
|
|
2
|
+
release: () => void;
|
|
3
|
+
};
|
|
4
|
+
declare const createGarbageCollectionGuard: <T>({ createError, }: {
|
|
5
|
+
createError: (args: {
|
|
6
|
+
info: T;
|
|
7
|
+
}) => Error;
|
|
8
|
+
}) => {
|
|
9
|
+
protect: ({ release: providedRelease, info }: {
|
|
10
|
+
release: () => void;
|
|
11
|
+
info: T;
|
|
12
|
+
}) => TInstance;
|
|
13
|
+
};
|
|
14
|
+
declare const createDefaultGarbageCollectedWithoutReleaseError: ({ name, info, releaseFunctionName, resourcesName, }: {
|
|
15
|
+
name: string;
|
|
16
|
+
info: string;
|
|
17
|
+
releaseFunctionName: string;
|
|
18
|
+
resourcesName: string;
|
|
19
|
+
}) => any;
|
|
20
|
+
export { createGarbageCollectionGuard, createDefaultGarbageCollectedWithoutReleaseError };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/*type TInstance = {
|
|
2
|
+
release: () => void;
|
|
3
|
+
};*/
|
|
4
|
+
|
|
5
|
+
const createGarbageCollectionGuard = /*<T>*/({
|
|
6
|
+
createError,
|
|
7
|
+
}/*: {
|
|
8
|
+
createError: (args: { info: T }) => Error,
|
|
9
|
+
}*/) => {
|
|
10
|
+
|
|
11
|
+
const instanceFinalizationRegistry = new FinalizationRegistry((info/*: T*/) => {
|
|
12
|
+
// this callback is called when a instance is garbage collected without release being called
|
|
13
|
+
throw createError({ info });
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const protect = ({
|
|
17
|
+
release: providedRelease,
|
|
18
|
+
info
|
|
19
|
+
}/*: { release: () => void; info: T }*/)/*: TInstance*/ => {
|
|
20
|
+
const release = () => {
|
|
21
|
+
providedRelease();
|
|
22
|
+
instanceFinalizationRegistry.unregister(release);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
instanceFinalizationRegistry.register(release, info, release);
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
release
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
protect
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const createDefaultGarbageCollectedWithoutReleaseError = ({
|
|
38
|
+
name,
|
|
39
|
+
info,
|
|
40
|
+
releaseFunctionName,
|
|
41
|
+
resourcesName,
|
|
42
|
+
}/*: {
|
|
43
|
+
name: string,
|
|
44
|
+
info: string,
|
|
45
|
+
releaseFunctionName: string,
|
|
46
|
+
resourcesName: string,
|
|
47
|
+
}*/) => {
|
|
48
|
+
|
|
49
|
+
let message = `${info} was garbage collected without calling release().`;
|
|
50
|
+
message += ` This would cause a memory leak -`;
|
|
51
|
+
message += ` therefore this raises an uncaught exception.`;
|
|
52
|
+
message += ` Please make sure to call ${releaseFunctionName}() on all ${resourcesName} when you are done with them.`;
|
|
53
|
+
message += ` Underlying resources should have still be cleaned up in case you are catching uncaught exceptions -`;
|
|
54
|
+
message += ` however, do not rely on this behavior (specifically for production code).`;
|
|
55
|
+
|
|
56
|
+
const error = Error(message);
|
|
57
|
+
error.name = name;
|
|
58
|
+
|
|
59
|
+
return error;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
createGarbageCollectionGuard,
|
|
64
|
+
createDefaultGarbageCollectedWithoutReleaseError
|
|
65
|
+
};
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.0.
|
|
2
|
+
"version": "0.0.4",
|
|
3
3
|
"name": "@k13engineering/linux-dmabuf",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "DMA buffer utilities for Node.js",
|
|
@@ -9,18 +9,24 @@
|
|
|
9
9
|
"main": "dist/lib/index.js",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "rm -rf dist/ && deno-node-build --root . --out dist/ --entry lib/index.ts",
|
|
12
|
-
"test": "c8 --reporter lcov --reporter html --reporter text --all --src lib/
|
|
12
|
+
"test": "c8 --reporter lcov --reporter html --reporter text --all --src lib/ --exclude lib/snippets --exclude test/ mocha test/**/*.ts",
|
|
13
13
|
"lint": "eslint ."
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@eslint/js": "^9.17.0",
|
|
17
17
|
"@k13engineering/releasetool": "^0.0.5",
|
|
18
|
+
"@types/mocha": "^10.0.10",
|
|
18
19
|
"@types/node": "^22.10.5",
|
|
19
20
|
"c8": "^10.1.3",
|
|
20
21
|
"deno-node": "^0.0.12",
|
|
22
|
+
"mocha": "^11.7.5",
|
|
21
23
|
"typescript-eslint": "^8.19.0"
|
|
22
24
|
},
|
|
23
|
-
"dependencies": {
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@k13engineering/po6-ioctl": "^0.0.2",
|
|
27
|
+
"@k13engineering/po6-mmap": "^0.0.7",
|
|
28
|
+
"syscall-napi": "^0.1.5"
|
|
29
|
+
},
|
|
24
30
|
"publishConfig": {
|
|
25
31
|
"access": "public"
|
|
26
32
|
},
|