@plasius/gpu-lock-free-queue
Advanced tools
+21
-0
@@ -23,2 +23,22 @@ # Changelog | ||
| ## [0.2.14] - 2026-03-14 | ||
| - **Added** | ||
| - `createDagJobGraph(...)` now derives `priorityLanes`, `jobIds`, | ||
| `unresolvedDependencyCount`, and `dependentCount` so host code can size | ||
| multi-root ready queues and inspect dependency fan-out directly. | ||
| - **Changed** | ||
| - DAG scheduler docs now describe the host-side graph contract in the same | ||
| terms as the GPU ready-queue implementation. | ||
| - Updated GitHub Actions workflows to run JavaScript actions on Node 24, | ||
| refreshed core workflow action versions, and switched Codecov uploads to | ||
| the Codecov CLI. | ||
| - **Fixed** | ||
| - (placeholder) | ||
| - **Security** | ||
| - (placeholder) | ||
| ## [0.2.13] - 2026-03-13 | ||
@@ -290,1 +310,2 @@ | ||
| [0.2.13]: https://github.com/Plasius-LTD/gpu-lock-free-queue/releases/tag/v0.2.13 | ||
| [0.2.14]: https://github.com/Plasius-LTD/gpu-lock-free-queue/releases/tag/v0.2.14 |
+31
-0
@@ -58,2 +58,28 @@ // src/index.cjs | ||
| } | ||
| function buildPriorityLanes(graphJobs) { | ||
| const lanes = /* @__PURE__ */ new Map(); | ||
| for (const job of graphJobs) { | ||
| const lane = lanes.get(job.priority) ?? { | ||
| priority: job.priority, | ||
| jobIds: [], | ||
| rootJobIds: [] | ||
| }; | ||
| lane.jobIds.push(job.id); | ||
| if (job.root) { | ||
| lane.rootJobIds.push(job.id); | ||
| } | ||
| lanes.set(job.priority, lane); | ||
| } | ||
| return Object.freeze( | ||
| [...lanes.values()].sort((left, right) => right.priority - left.priority).map( | ||
| (lane) => Object.freeze({ | ||
| priority: lane.priority, | ||
| jobIds: Object.freeze([...lane.jobIds]), | ||
| rootJobIds: Object.freeze([...lane.rootJobIds]), | ||
| jobCount: lane.jobIds.length, | ||
| rootCount: lane.rootJobIds.length | ||
| }) | ||
| ) | ||
| ); | ||
| } | ||
| async function loadWgslAsset(assetUrl, options = {}) { | ||
@@ -168,5 +194,8 @@ const { url = assetUrl, fetcher = globalThis.fetch } = options ?? {}; | ||
| dependencyCount: job.dependencies.length, | ||
| unresolvedDependencyCount: job.dependencies.length, | ||
| dependentCount: (dependentsById.get(job.id) ?? []).length, | ||
| root: job.dependencies.length === 0 | ||
| }) | ||
| ); | ||
| const priorityLanes = buildPriorityLanes(graphJobs); | ||
| return Object.freeze({ | ||
@@ -176,4 +205,6 @@ mode: "dag", | ||
| maxPriority, | ||
| jobIds: Object.freeze(graphJobs.map((job) => job.id)), | ||
| roots: Object.freeze(graphJobs.filter((job) => job.root).map((job) => job.id)), | ||
| topologicalOrder: Object.freeze(topo), | ||
| priorityLanes, | ||
| jobs: Object.freeze(graphJobs) | ||
@@ -180,0 +211,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.cjs"],"sourcesContent":["const { pathToFileURL, fileURLToPath } = require(\"node:url\");\nconst { readFile } = require(\"node:fs/promises\");\n\nconst queueWgslUrl = new URL(\"./queue.wgsl\", pathToFileURL(__filename));\nconst dagQueueWgslUrl = new URL(\"./dag-queue.wgsl\", pathToFileURL(__filename));\nconst schedulerModes = Object.freeze([\"flat\", \"dag\"]);\nconst IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/u;\n\nfunction assertSchedulerMode(mode) {\n const resolved = mode ?? \"flat\";\n if (!schedulerModes.includes(resolved)) {\n throw new Error(`mode must be one of: ${schedulerModes.join(\", \")}.`);\n }\n return resolved;\n}\n\nfunction assertIdentifier(name, value) {\n if (typeof value !== \"string\" || !IDENTIFIER_PATTERN.test(value)) {\n throw new Error(\n `${name} must match ${IDENTIFIER_PATTERN.toString()} and be at most 64 characters long.`\n );\n }\n return value;\n}\n\nfunction readNonNegativeInteger(name, value) {\n if (value === undefined) {\n return 0;\n }\n if (\n typeof value !== \"number\" ||\n !Number.isInteger(value) ||\n value < 0 ||\n !Number.isFinite(value)\n ) {\n throw new Error(`${name} must be a non-negative integer.`);\n }\n return value;\n}\n\nfunction normalizeDependencies(name, value) {\n if (value === undefined) {\n return [];\n }\n if (!Array.isArray(value)) {\n throw new Error(`${name} must be an array of dependency ids.`);\n }\n return [...new Set(value.map((entry, index) =>\n assertIdentifier(`${name}[${index}]`, entry)\n ))];\n}\n\nfunction resolveGraphJobId(job, index) {\n if (typeof job.id === \"string\") {\n return assertIdentifier(`jobs[${index}].id`, job.id);\n }\n if (typeof job.label === \"string\") {\n return assertIdentifier(`jobs[${index}].label`, job.label);\n }\n if (typeof job.key === \"string\") {\n return assertIdentifier(`jobs[${index}].key`, job.key);\n }\n if (typeof job.jobType === \"string\") {\n return assertIdentifier(`jobs[${index}].jobType`, job.jobType);\n }\n return assertIdentifier(`jobs[${index}].id`, `job-${index}`);\n}\n\nasync function loadWgslAsset(assetUrl, options = {}) {\n const { url = assetUrl, fetcher = globalThis.fetch } = options ?? {};\n const wgslUrl = url instanceof URL ? url : new URL(url, assetUrl);\n\n if (!fetcher || wgslUrl.protocol === \"file:\") {\n return readFile(fileURLToPath(wgslUrl), \"utf8\");\n }\n\n const response = await fetcher(wgslUrl);\n if (!response.ok) {\n const status = \"status\" in response ? response.status : \"unknown\";\n const statusText = \"statusText\" in response ? response.statusText : \"\";\n const detail = statusText ? `${status} ${statusText}` : `${status}`;\n throw new Error(`Failed to load WGSL (${detail})`);\n }\n return response.text();\n}\n\nasync function loadQueueWgsl(options = {}) {\n return loadWgslAsset(queueWgslUrl, options);\n}\n\nasync function loadDagQueueWgsl(options = {}) {\n return loadWgslAsset(dagQueueWgslUrl, options);\n}\n\nasync function loadSchedulerWgsl(options = {}) {\n const { mode = \"flat\", ...rest } = options ?? {};\n const resolvedMode = assertSchedulerMode(mode);\n if (resolvedMode === \"dag\") {\n return loadDagQueueWgsl(rest);\n }\n return loadQueueWgsl(rest);\n}\n\nfunction createDagJobGraph(jobs = []) {\n if (!Array.isArray(jobs)) {\n throw new Error(\"jobs must be an array.\");\n }\n\n const normalized = jobs.map((job, index) => {\n if (!job || typeof job !== \"object\" || Array.isArray(job)) {\n throw new Error(`jobs[${index}] must be an object.`);\n }\n\n const id = resolveGraphJobId(job, index);\n const dependencies = normalizeDependencies(\n `jobs[${index}].dependencies`,\n job.dependencies ?? job.dependsOn\n );\n const priority = readNonNegativeInteger(\n `jobs[${index}].priority`,\n job.priority\n );\n\n return {\n id,\n key: typeof job.key === \"string\" ? job.key : undefined,\n label: typeof job.label === \"string\" ? job.label : undefined,\n jobType: job.jobType,\n queueClass: typeof job.queueClass === \"string\" ? job.queueClass : undefined,\n priority,\n dependencies,\n };\n });\n\n const ids = new Set();\n for (const job of normalized) {\n if (ids.has(job.id)) {\n throw new Error(`Duplicate DAG job id detected: ${job.id}`);\n }\n ids.add(job.id);\n }\n\n for (const job of normalized) {\n for (const dependency of job.dependencies) {\n if (!ids.has(dependency)) {\n throw new Error(\n `Job \"${job.id}\" depends on unknown job \"${dependency}\".`\n );\n }\n if (dependency === job.id) {\n throw new Error(`Job \"${job.id}\" cannot depend on itself.`);\n }\n }\n }\n\n const dependentsById = new Map(normalized.map((job) => [job.id, []]));\n const indegree = new Map(normalized.map((job) => [job.id, job.dependencies.length]));\n for (const job of normalized) {\n for (const dependency of job.dependencies) {\n dependentsById.get(dependency).push(job.id);\n }\n }\n\n const ready = normalized\n .filter((job) => job.dependencies.length === 0)\n .map((job) => job.id);\n const topo = [];\n const queue = [...ready];\n while (queue.length > 0) {\n const currentId = queue.shift();\n topo.push(currentId);\n for (const dependentId of dependentsById.get(currentId) ?? []) {\n const next = (indegree.get(dependentId) ?? 0) - 1;\n indegree.set(dependentId, next);\n if (next === 0) {\n queue.push(dependentId);\n }\n }\n }\n\n if (topo.length !== normalized.length) {\n throw new Error(\"DAG graph contains a cycle.\");\n }\n\n const maxPriority = normalized.reduce(\n (current, job) => Math.max(current, job.priority),\n 0\n );\n\n const graphJobs = normalized.map((job) =>\n Object.freeze({\n ...job,\n dependencies: Object.freeze([...job.dependencies]),\n dependents: Object.freeze([...(dependentsById.get(job.id) ?? [])]),\n dependencyCount: job.dependencies.length,\n root: job.dependencies.length === 0,\n })\n );\n\n return Object.freeze({\n mode: \"dag\",\n jobCount: graphJobs.length,\n maxPriority,\n roots: Object.freeze(graphJobs.filter((job) => job.root).map((job) => job.id)),\n topologicalOrder: Object.freeze(topo),\n jobs: Object.freeze(graphJobs),\n });\n}\n\nmodule.exports = {\n queueWgslUrl,\n dagQueueWgslUrl,\n schedulerModes,\n loadQueueWgsl,\n loadDagQueueWgsl,\n loadSchedulerWgsl,\n createDagJobGraph,\n};\n"],"mappings":"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","names":[]} | ||
| {"version":3,"sources":["../src/index.cjs"],"sourcesContent":["const { pathToFileURL, fileURLToPath } = require(\"node:url\");\nconst { readFile } = require(\"node:fs/promises\");\n\nconst queueWgslUrl = new URL(\"./queue.wgsl\", pathToFileURL(__filename));\nconst dagQueueWgslUrl = new URL(\"./dag-queue.wgsl\", pathToFileURL(__filename));\nconst schedulerModes = Object.freeze([\"flat\", \"dag\"]);\nconst IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/u;\n\nfunction assertSchedulerMode(mode) {\n const resolved = mode ?? \"flat\";\n if (!schedulerModes.includes(resolved)) {\n throw new Error(`mode must be one of: ${schedulerModes.join(\", \")}.`);\n }\n return resolved;\n}\n\nfunction assertIdentifier(name, value) {\n if (typeof value !== \"string\" || !IDENTIFIER_PATTERN.test(value)) {\n throw new Error(\n `${name} must match ${IDENTIFIER_PATTERN.toString()} and be at most 64 characters long.`\n );\n }\n return value;\n}\n\nfunction readNonNegativeInteger(name, value) {\n if (value === undefined) {\n return 0;\n }\n if (\n typeof value !== \"number\" ||\n !Number.isInteger(value) ||\n value < 0 ||\n !Number.isFinite(value)\n ) {\n throw new Error(`${name} must be a non-negative integer.`);\n }\n return value;\n}\n\nfunction normalizeDependencies(name, value) {\n if (value === undefined) {\n return [];\n }\n if (!Array.isArray(value)) {\n throw new Error(`${name} must be an array of dependency ids.`);\n }\n return [...new Set(value.map((entry, index) =>\n assertIdentifier(`${name}[${index}]`, entry)\n ))];\n}\n\nfunction resolveGraphJobId(job, index) {\n if (typeof job.id === \"string\") {\n return assertIdentifier(`jobs[${index}].id`, job.id);\n }\n if (typeof job.label === \"string\") {\n return assertIdentifier(`jobs[${index}].label`, job.label);\n }\n if (typeof job.key === \"string\") {\n return assertIdentifier(`jobs[${index}].key`, job.key);\n }\n if (typeof job.jobType === \"string\") {\n return assertIdentifier(`jobs[${index}].jobType`, job.jobType);\n }\n return assertIdentifier(`jobs[${index}].id`, `job-${index}`);\n}\n\nfunction buildPriorityLanes(graphJobs) {\n const lanes = new Map();\n\n for (const job of graphJobs) {\n const lane = lanes.get(job.priority) ?? {\n priority: job.priority,\n jobIds: [],\n rootJobIds: [],\n };\n lane.jobIds.push(job.id);\n if (job.root) {\n lane.rootJobIds.push(job.id);\n }\n lanes.set(job.priority, lane);\n }\n\n return Object.freeze(\n [...lanes.values()]\n .sort((left, right) => right.priority - left.priority)\n .map((lane) =>\n Object.freeze({\n priority: lane.priority,\n jobIds: Object.freeze([...lane.jobIds]),\n rootJobIds: Object.freeze([...lane.rootJobIds]),\n jobCount: lane.jobIds.length,\n rootCount: lane.rootJobIds.length,\n })\n )\n );\n}\n\nasync function loadWgslAsset(assetUrl, options = {}) {\n const { url = assetUrl, fetcher = globalThis.fetch } = options ?? {};\n const wgslUrl = url instanceof URL ? url : new URL(url, assetUrl);\n\n if (!fetcher || wgslUrl.protocol === \"file:\") {\n return readFile(fileURLToPath(wgslUrl), \"utf8\");\n }\n\n const response = await fetcher(wgslUrl);\n if (!response.ok) {\n const status = \"status\" in response ? response.status : \"unknown\";\n const statusText = \"statusText\" in response ? response.statusText : \"\";\n const detail = statusText ? `${status} ${statusText}` : `${status}`;\n throw new Error(`Failed to load WGSL (${detail})`);\n }\n return response.text();\n}\n\nasync function loadQueueWgsl(options = {}) {\n return loadWgslAsset(queueWgslUrl, options);\n}\n\nasync function loadDagQueueWgsl(options = {}) {\n return loadWgslAsset(dagQueueWgslUrl, options);\n}\n\nasync function loadSchedulerWgsl(options = {}) {\n const { mode = \"flat\", ...rest } = options ?? {};\n const resolvedMode = assertSchedulerMode(mode);\n if (resolvedMode === \"dag\") {\n return loadDagQueueWgsl(rest);\n }\n return loadQueueWgsl(rest);\n}\n\nfunction createDagJobGraph(jobs = []) {\n if (!Array.isArray(jobs)) {\n throw new Error(\"jobs must be an array.\");\n }\n\n const normalized = jobs.map((job, index) => {\n if (!job || typeof job !== \"object\" || Array.isArray(job)) {\n throw new Error(`jobs[${index}] must be an object.`);\n }\n\n const id = resolveGraphJobId(job, index);\n const dependencies = normalizeDependencies(\n `jobs[${index}].dependencies`,\n job.dependencies ?? job.dependsOn\n );\n const priority = readNonNegativeInteger(\n `jobs[${index}].priority`,\n job.priority\n );\n\n return {\n id,\n key: typeof job.key === \"string\" ? job.key : undefined,\n label: typeof job.label === \"string\" ? job.label : undefined,\n jobType: job.jobType,\n queueClass: typeof job.queueClass === \"string\" ? job.queueClass : undefined,\n priority,\n dependencies,\n };\n });\n\n const ids = new Set();\n for (const job of normalized) {\n if (ids.has(job.id)) {\n throw new Error(`Duplicate DAG job id detected: ${job.id}`);\n }\n ids.add(job.id);\n }\n\n for (const job of normalized) {\n for (const dependency of job.dependencies) {\n if (!ids.has(dependency)) {\n throw new Error(\n `Job \"${job.id}\" depends on unknown job \"${dependency}\".`\n );\n }\n if (dependency === job.id) {\n throw new Error(`Job \"${job.id}\" cannot depend on itself.`);\n }\n }\n }\n\n const dependentsById = new Map(normalized.map((job) => [job.id, []]));\n const indegree = new Map(normalized.map((job) => [job.id, job.dependencies.length]));\n for (const job of normalized) {\n for (const dependency of job.dependencies) {\n dependentsById.get(dependency).push(job.id);\n }\n }\n\n const ready = normalized\n .filter((job) => job.dependencies.length === 0)\n .map((job) => job.id);\n const topo = [];\n const queue = [...ready];\n while (queue.length > 0) {\n const currentId = queue.shift();\n topo.push(currentId);\n for (const dependentId of dependentsById.get(currentId) ?? []) {\n const next = (indegree.get(dependentId) ?? 0) - 1;\n indegree.set(dependentId, next);\n if (next === 0) {\n queue.push(dependentId);\n }\n }\n }\n\n if (topo.length !== normalized.length) {\n throw new Error(\"DAG graph contains a cycle.\");\n }\n\n const maxPriority = normalized.reduce(\n (current, job) => Math.max(current, job.priority),\n 0\n );\n\n const graphJobs = normalized.map((job) =>\n Object.freeze({\n ...job,\n dependencies: Object.freeze([...job.dependencies]),\n dependents: Object.freeze([...(dependentsById.get(job.id) ?? [])]),\n dependencyCount: job.dependencies.length,\n unresolvedDependencyCount: job.dependencies.length,\n dependentCount: (dependentsById.get(job.id) ?? []).length,\n root: job.dependencies.length === 0,\n })\n );\n\n const priorityLanes = buildPriorityLanes(graphJobs);\n\n return Object.freeze({\n mode: \"dag\",\n jobCount: graphJobs.length,\n maxPriority,\n jobIds: Object.freeze(graphJobs.map((job) => job.id)),\n roots: Object.freeze(graphJobs.filter((job) => job.root).map((job) => job.id)),\n topologicalOrder: Object.freeze(topo),\n priorityLanes,\n jobs: Object.freeze(graphJobs),\n });\n}\n\nmodule.exports = {\n queueWgslUrl,\n dagQueueWgslUrl,\n schedulerModes,\n loadQueueWgsl,\n loadDagQueueWgsl,\n loadSchedulerWgsl,\n createDagJobGraph,\n};\n"],"mappings":"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","names":[]} |
+31
-0
@@ -58,2 +58,28 @@ // src/index.js | ||
| } | ||
| function buildPriorityLanes(graphJobs) { | ||
| const lanes = /* @__PURE__ */ new Map(); | ||
| for (const job of graphJobs) { | ||
| const lane = lanes.get(job.priority) ?? { | ||
| priority: job.priority, | ||
| jobIds: [], | ||
| rootJobIds: [] | ||
| }; | ||
| lane.jobIds.push(job.id); | ||
| if (job.root) { | ||
| lane.rootJobIds.push(job.id); | ||
| } | ||
| lanes.set(job.priority, lane); | ||
| } | ||
| return Object.freeze( | ||
| [...lanes.values()].sort((left, right) => right.priority - left.priority).map( | ||
| (lane) => Object.freeze({ | ||
| priority: lane.priority, | ||
| jobIds: Object.freeze([...lane.jobIds]), | ||
| rootJobIds: Object.freeze([...lane.rootJobIds]), | ||
| jobCount: lane.jobIds.length, | ||
| rootCount: lane.rootJobIds.length | ||
| }) | ||
| ) | ||
| ); | ||
| } | ||
| async function loadWgslAsset(assetUrl, options = {}) { | ||
@@ -170,5 +196,8 @@ const { url = assetUrl, fetcher = globalThis.fetch } = options ?? {}; | ||
| dependencyCount: job.dependencies.length, | ||
| unresolvedDependencyCount: job.dependencies.length, | ||
| dependentCount: (dependentsById.get(job.id) ?? []).length, | ||
| root: job.dependencies.length === 0 | ||
| }) | ||
| ); | ||
| const priorityLanes = buildPriorityLanes(graphJobs); | ||
| return Object.freeze({ | ||
@@ -178,4 +207,6 @@ mode: "dag", | ||
| maxPriority, | ||
| jobIds: Object.freeze(graphJobs.map((job) => job.id)), | ||
| roots: Object.freeze(graphJobs.filter((job) => job.root).map((job) => job.id)), | ||
| topologicalOrder: Object.freeze(topo), | ||
| priorityLanes, | ||
| jobs: Object.freeze(graphJobs) | ||
@@ -182,0 +213,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.js"],"sourcesContent":["export const queueWgslUrl = new URL(\"./queue.wgsl\", import.meta.url);\nexport const dagQueueWgslUrl = new URL(\"./dag-queue.wgsl\", import.meta.url);\nexport const schedulerModes = Object.freeze([\"flat\", \"dag\"]);\n\nconst IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/u;\n\nfunction assertSchedulerMode(mode) {\n const resolved = mode ?? \"flat\";\n if (!schedulerModes.includes(resolved)) {\n throw new Error(\n `mode must be one of: ${schedulerModes.join(\", \")}.`\n );\n }\n return resolved;\n}\n\nfunction assertIdentifier(name, value) {\n if (typeof value !== \"string\" || !IDENTIFIER_PATTERN.test(value)) {\n throw new Error(\n `${name} must match ${IDENTIFIER_PATTERN.toString()} and be at most 64 characters long.`\n );\n }\n return value;\n}\n\nfunction readNonNegativeInteger(name, value) {\n if (value === undefined) {\n return 0;\n }\n if (\n typeof value !== \"number\" ||\n !Number.isInteger(value) ||\n value < 0 ||\n !Number.isFinite(value)\n ) {\n throw new Error(`${name} must be a non-negative integer.`);\n }\n return value;\n}\n\nfunction normalizeDependencies(name, value) {\n if (value === undefined) {\n return [];\n }\n if (!Array.isArray(value)) {\n throw new Error(`${name} must be an array of dependency ids.`);\n }\n return [...new Set(value.map((entry, index) =>\n assertIdentifier(`${name}[${index}]`, entry)\n ))];\n}\n\nfunction resolveGraphJobId(job, index) {\n if (typeof job.id === \"string\") {\n return assertIdentifier(`jobs[${index}].id`, job.id);\n }\n if (typeof job.label === \"string\") {\n return assertIdentifier(`jobs[${index}].label`, job.label);\n }\n if (typeof job.key === \"string\") {\n return assertIdentifier(`jobs[${index}].key`, job.key);\n }\n if (typeof job.jobType === \"string\") {\n return assertIdentifier(`jobs[${index}].jobType`, job.jobType);\n }\n return assertIdentifier(`jobs[${index}].id`, `job-${index}`);\n}\n\nasync function loadWgslAsset(assetUrl, options = {}) {\n const { url = assetUrl, fetcher = globalThis.fetch } = options ?? {};\n const wgslUrl = url instanceof URL ? url : new URL(url, assetUrl);\n\n if (!fetcher || wgslUrl.protocol === \"file:\") {\n const { readFile } = await import(\"node:fs/promises\");\n const { fileURLToPath } = await import(\"node:url\");\n return readFile(fileURLToPath(wgslUrl), \"utf8\");\n }\n\n const response = await fetcher(wgslUrl);\n if (!response.ok) {\n const status = \"status\" in response ? response.status : \"unknown\";\n const statusText = \"statusText\" in response ? response.statusText : \"\";\n const detail = statusText ? `${status} ${statusText}` : `${status}`;\n throw new Error(`Failed to load WGSL (${detail})`);\n }\n return response.text();\n}\n\nexport async function loadQueueWgsl(options = {}) {\n return loadWgslAsset(queueWgslUrl, options);\n}\n\nexport async function loadDagQueueWgsl(options = {}) {\n return loadWgslAsset(dagQueueWgslUrl, options);\n}\n\nexport async function loadSchedulerWgsl(options = {}) {\n const { mode = \"flat\", ...rest } = options ?? {};\n const resolvedMode = assertSchedulerMode(mode);\n if (resolvedMode === \"dag\") {\n return loadDagQueueWgsl(rest);\n }\n return loadQueueWgsl(rest);\n}\n\nexport function createDagJobGraph(jobs = []) {\n if (!Array.isArray(jobs)) {\n throw new Error(\"jobs must be an array.\");\n }\n\n const normalized = jobs.map((job, index) => {\n if (!job || typeof job !== \"object\" || Array.isArray(job)) {\n throw new Error(`jobs[${index}] must be an object.`);\n }\n\n const id = resolveGraphJobId(job, index);\n const dependencies = normalizeDependencies(\n `jobs[${index}].dependencies`,\n job.dependencies ?? job.dependsOn\n );\n const priority = readNonNegativeInteger(\n `jobs[${index}].priority`,\n job.priority\n );\n\n return {\n id,\n key: typeof job.key === \"string\" ? job.key : undefined,\n label: typeof job.label === \"string\" ? job.label : undefined,\n jobType: job.jobType,\n queueClass: typeof job.queueClass === \"string\" ? job.queueClass : undefined,\n priority,\n dependencies,\n };\n });\n\n const ids = new Set();\n for (const job of normalized) {\n if (ids.has(job.id)) {\n throw new Error(`Duplicate DAG job id detected: ${job.id}`);\n }\n ids.add(job.id);\n }\n\n for (const job of normalized) {\n for (const dependency of job.dependencies) {\n if (!ids.has(dependency)) {\n throw new Error(\n `Job \"${job.id}\" depends on unknown job \"${dependency}\".`\n );\n }\n if (dependency === job.id) {\n throw new Error(`Job \"${job.id}\" cannot depend on itself.`);\n }\n }\n }\n\n const dependentsById = new Map(normalized.map((job) => [job.id, []]));\n const indegree = new Map(normalized.map((job) => [job.id, job.dependencies.length]));\n for (const job of normalized) {\n for (const dependency of job.dependencies) {\n dependentsById.get(dependency).push(job.id);\n }\n }\n\n const ready = normalized\n .filter((job) => job.dependencies.length === 0)\n .map((job) => job.id);\n const topo = [];\n const queue = [...ready];\n while (queue.length > 0) {\n const currentId = queue.shift();\n topo.push(currentId);\n for (const dependentId of dependentsById.get(currentId) ?? []) {\n const next = (indegree.get(dependentId) ?? 0) - 1;\n indegree.set(dependentId, next);\n if (next === 0) {\n queue.push(dependentId);\n }\n }\n }\n\n if (topo.length !== normalized.length) {\n throw new Error(\"DAG graph contains a cycle.\");\n }\n\n const maxPriority = normalized.reduce(\n (current, job) => Math.max(current, job.priority),\n 0\n );\n\n const graphJobs = normalized.map((job) =>\n Object.freeze({\n ...job,\n dependencies: Object.freeze([...job.dependencies]),\n dependents: Object.freeze([...(dependentsById.get(job.id) ?? [])]),\n dependencyCount: job.dependencies.length,\n root: job.dependencies.length === 0,\n })\n );\n\n return Object.freeze({\n mode: \"dag\",\n jobCount: graphJobs.length,\n maxPriority,\n roots: Object.freeze(graphJobs.filter((job) => job.root).map((job) => job.id)),\n topologicalOrder: Object.freeze(topo),\n jobs: Object.freeze(graphJobs),\n });\n}\n"],"mappings":"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","names":[]} | ||
| {"version":3,"sources":["../src/index.js"],"sourcesContent":["export const queueWgslUrl = new URL(\"./queue.wgsl\", import.meta.url);\nexport const dagQueueWgslUrl = new URL(\"./dag-queue.wgsl\", import.meta.url);\nexport const schedulerModes = Object.freeze([\"flat\", \"dag\"]);\n\nconst IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/u;\n\nfunction assertSchedulerMode(mode) {\n const resolved = mode ?? \"flat\";\n if (!schedulerModes.includes(resolved)) {\n throw new Error(\n `mode must be one of: ${schedulerModes.join(\", \")}.`\n );\n }\n return resolved;\n}\n\nfunction assertIdentifier(name, value) {\n if (typeof value !== \"string\" || !IDENTIFIER_PATTERN.test(value)) {\n throw new Error(\n `${name} must match ${IDENTIFIER_PATTERN.toString()} and be at most 64 characters long.`\n );\n }\n return value;\n}\n\nfunction readNonNegativeInteger(name, value) {\n if (value === undefined) {\n return 0;\n }\n if (\n typeof value !== \"number\" ||\n !Number.isInteger(value) ||\n value < 0 ||\n !Number.isFinite(value)\n ) {\n throw new Error(`${name} must be a non-negative integer.`);\n }\n return value;\n}\n\nfunction normalizeDependencies(name, value) {\n if (value === undefined) {\n return [];\n }\n if (!Array.isArray(value)) {\n throw new Error(`${name} must be an array of dependency ids.`);\n }\n return [...new Set(value.map((entry, index) =>\n assertIdentifier(`${name}[${index}]`, entry)\n ))];\n}\n\nfunction resolveGraphJobId(job, index) {\n if (typeof job.id === \"string\") {\n return assertIdentifier(`jobs[${index}].id`, job.id);\n }\n if (typeof job.label === \"string\") {\n return assertIdentifier(`jobs[${index}].label`, job.label);\n }\n if (typeof job.key === \"string\") {\n return assertIdentifier(`jobs[${index}].key`, job.key);\n }\n if (typeof job.jobType === \"string\") {\n return assertIdentifier(`jobs[${index}].jobType`, job.jobType);\n }\n return assertIdentifier(`jobs[${index}].id`, `job-${index}`);\n}\n\nfunction buildPriorityLanes(graphJobs) {\n const lanes = new Map();\n\n for (const job of graphJobs) {\n const lane = lanes.get(job.priority) ?? {\n priority: job.priority,\n jobIds: [],\n rootJobIds: [],\n };\n lane.jobIds.push(job.id);\n if (job.root) {\n lane.rootJobIds.push(job.id);\n }\n lanes.set(job.priority, lane);\n }\n\n return Object.freeze(\n [...lanes.values()]\n .sort((left, right) => right.priority - left.priority)\n .map((lane) =>\n Object.freeze({\n priority: lane.priority,\n jobIds: Object.freeze([...lane.jobIds]),\n rootJobIds: Object.freeze([...lane.rootJobIds]),\n jobCount: lane.jobIds.length,\n rootCount: lane.rootJobIds.length,\n })\n )\n );\n}\n\nasync function loadWgslAsset(assetUrl, options = {}) {\n const { url = assetUrl, fetcher = globalThis.fetch } = options ?? {};\n const wgslUrl = url instanceof URL ? url : new URL(url, assetUrl);\n\n if (!fetcher || wgslUrl.protocol === \"file:\") {\n const { readFile } = await import(\"node:fs/promises\");\n const { fileURLToPath } = await import(\"node:url\");\n return readFile(fileURLToPath(wgslUrl), \"utf8\");\n }\n\n const response = await fetcher(wgslUrl);\n if (!response.ok) {\n const status = \"status\" in response ? response.status : \"unknown\";\n const statusText = \"statusText\" in response ? response.statusText : \"\";\n const detail = statusText ? `${status} ${statusText}` : `${status}`;\n throw new Error(`Failed to load WGSL (${detail})`);\n }\n return response.text();\n}\n\nexport async function loadQueueWgsl(options = {}) {\n return loadWgslAsset(queueWgslUrl, options);\n}\n\nexport async function loadDagQueueWgsl(options = {}) {\n return loadWgslAsset(dagQueueWgslUrl, options);\n}\n\nexport async function loadSchedulerWgsl(options = {}) {\n const { mode = \"flat\", ...rest } = options ?? {};\n const resolvedMode = assertSchedulerMode(mode);\n if (resolvedMode === \"dag\") {\n return loadDagQueueWgsl(rest);\n }\n return loadQueueWgsl(rest);\n}\n\nexport function createDagJobGraph(jobs = []) {\n if (!Array.isArray(jobs)) {\n throw new Error(\"jobs must be an array.\");\n }\n\n const normalized = jobs.map((job, index) => {\n if (!job || typeof job !== \"object\" || Array.isArray(job)) {\n throw new Error(`jobs[${index}] must be an object.`);\n }\n\n const id = resolveGraphJobId(job, index);\n const dependencies = normalizeDependencies(\n `jobs[${index}].dependencies`,\n job.dependencies ?? job.dependsOn\n );\n const priority = readNonNegativeInteger(\n `jobs[${index}].priority`,\n job.priority\n );\n\n return {\n id,\n key: typeof job.key === \"string\" ? job.key : undefined,\n label: typeof job.label === \"string\" ? job.label : undefined,\n jobType: job.jobType,\n queueClass: typeof job.queueClass === \"string\" ? job.queueClass : undefined,\n priority,\n dependencies,\n };\n });\n\n const ids = new Set();\n for (const job of normalized) {\n if (ids.has(job.id)) {\n throw new Error(`Duplicate DAG job id detected: ${job.id}`);\n }\n ids.add(job.id);\n }\n\n for (const job of normalized) {\n for (const dependency of job.dependencies) {\n if (!ids.has(dependency)) {\n throw new Error(\n `Job \"${job.id}\" depends on unknown job \"${dependency}\".`\n );\n }\n if (dependency === job.id) {\n throw new Error(`Job \"${job.id}\" cannot depend on itself.`);\n }\n }\n }\n\n const dependentsById = new Map(normalized.map((job) => [job.id, []]));\n const indegree = new Map(normalized.map((job) => [job.id, job.dependencies.length]));\n for (const job of normalized) {\n for (const dependency of job.dependencies) {\n dependentsById.get(dependency).push(job.id);\n }\n }\n\n const ready = normalized\n .filter((job) => job.dependencies.length === 0)\n .map((job) => job.id);\n const topo = [];\n const queue = [...ready];\n while (queue.length > 0) {\n const currentId = queue.shift();\n topo.push(currentId);\n for (const dependentId of dependentsById.get(currentId) ?? []) {\n const next = (indegree.get(dependentId) ?? 0) - 1;\n indegree.set(dependentId, next);\n if (next === 0) {\n queue.push(dependentId);\n }\n }\n }\n\n if (topo.length !== normalized.length) {\n throw new Error(\"DAG graph contains a cycle.\");\n }\n\n const maxPriority = normalized.reduce(\n (current, job) => Math.max(current, job.priority),\n 0\n );\n\n const graphJobs = normalized.map((job) =>\n Object.freeze({\n ...job,\n dependencies: Object.freeze([...job.dependencies]),\n dependents: Object.freeze([...(dependentsById.get(job.id) ?? [])]),\n dependencyCount: job.dependencies.length,\n unresolvedDependencyCount: job.dependencies.length,\n dependentCount: (dependentsById.get(job.id) ?? []).length,\n root: job.dependencies.length === 0,\n })\n );\n\n const priorityLanes = buildPriorityLanes(graphJobs);\n\n return Object.freeze({\n mode: \"dag\",\n jobCount: graphJobs.length,\n maxPriority,\n jobIds: Object.freeze(graphJobs.map((job) => job.id)),\n roots: Object.freeze(graphJobs.filter((job) => job.root).map((job) => job.id)),\n topologicalOrder: Object.freeze(topo),\n priorityLanes,\n jobs: Object.freeze(graphJobs),\n });\n}\n"],"mappings":"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","names":[]} |
+1
-1
| { | ||
| "name": "@plasius/gpu-lock-free-queue", | ||
| "version": "0.2.13", | ||
| "version": "0.2.14", | ||
| "description": "WebGPU lock-free queue assets for flat and DAG-ready GPU job scheduling.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
+12
-0
@@ -46,2 +46,4 @@ # @plasius/gpu-lock-free-queue | ||
| console.log(graph.roots); | ||
| console.log(graph.priorityLanes); | ||
| const dagSchedulerWgsl = await loadDagQueueWgsl(); | ||
@@ -67,2 +69,12 @@ const selectedWgsl = await loadSchedulerWgsl({ mode: graph.mode }); | ||
| The JS graph helper is the canonical preflight contract for DAG metadata. It | ||
| returns: | ||
| - `jobIds` for stable upload order | ||
| - `roots` for the initial runnable set | ||
| - `topologicalOrder` for validation and planning | ||
| - `priorityLanes` so callers can size ready queues per priority bucket | ||
| - per-job `dependencies`, `dependents`, `dependencyCount`, | ||
| `unresolvedDependencyCount`, and `dependentCount` | ||
| ## Buffer layout (breaking change in v0.4.0) | ||
@@ -69,0 +81,0 @@ Bindings are: |
+37
-0
@@ -69,2 +69,33 @@ const { pathToFileURL, fileURLToPath } = require("node:url"); | ||
| function buildPriorityLanes(graphJobs) { | ||
| const lanes = new Map(); | ||
| for (const job of graphJobs) { | ||
| const lane = lanes.get(job.priority) ?? { | ||
| priority: job.priority, | ||
| jobIds: [], | ||
| rootJobIds: [], | ||
| }; | ||
| lane.jobIds.push(job.id); | ||
| if (job.root) { | ||
| lane.rootJobIds.push(job.id); | ||
| } | ||
| lanes.set(job.priority, lane); | ||
| } | ||
| return Object.freeze( | ||
| [...lanes.values()] | ||
| .sort((left, right) => right.priority - left.priority) | ||
| .map((lane) => | ||
| Object.freeze({ | ||
| priority: lane.priority, | ||
| jobIds: Object.freeze([...lane.jobIds]), | ||
| rootJobIds: Object.freeze([...lane.rootJobIds]), | ||
| jobCount: lane.jobIds.length, | ||
| rootCount: lane.rootJobIds.length, | ||
| }) | ||
| ) | ||
| ); | ||
| } | ||
| async function loadWgslAsset(assetUrl, options = {}) { | ||
@@ -197,2 +228,4 @@ const { url = assetUrl, fetcher = globalThis.fetch } = options ?? {}; | ||
| dependencyCount: job.dependencies.length, | ||
| unresolvedDependencyCount: job.dependencies.length, | ||
| dependentCount: (dependentsById.get(job.id) ?? []).length, | ||
| root: job.dependencies.length === 0, | ||
@@ -202,2 +235,4 @@ }) | ||
| const priorityLanes = buildPriorityLanes(graphJobs); | ||
| return Object.freeze({ | ||
@@ -207,4 +242,6 @@ mode: "dag", | ||
| maxPriority, | ||
| jobIds: Object.freeze(graphJobs.map((job) => job.id)), | ||
| roots: Object.freeze(graphJobs.filter((job) => job.root).map((job) => job.id)), | ||
| topologicalOrder: Object.freeze(topo), | ||
| priorityLanes, | ||
| jobs: Object.freeze(graphJobs), | ||
@@ -211,0 +248,0 @@ }); |
+37
-0
@@ -69,2 +69,33 @@ export const queueWgslUrl = new URL("./queue.wgsl", import.meta.url); | ||
| function buildPriorityLanes(graphJobs) { | ||
| const lanes = new Map(); | ||
| for (const job of graphJobs) { | ||
| const lane = lanes.get(job.priority) ?? { | ||
| priority: job.priority, | ||
| jobIds: [], | ||
| rootJobIds: [], | ||
| }; | ||
| lane.jobIds.push(job.id); | ||
| if (job.root) { | ||
| lane.rootJobIds.push(job.id); | ||
| } | ||
| lanes.set(job.priority, lane); | ||
| } | ||
| return Object.freeze( | ||
| [...lanes.values()] | ||
| .sort((left, right) => right.priority - left.priority) | ||
| .map((lane) => | ||
| Object.freeze({ | ||
| priority: lane.priority, | ||
| jobIds: Object.freeze([...lane.jobIds]), | ||
| rootJobIds: Object.freeze([...lane.rootJobIds]), | ||
| jobCount: lane.jobIds.length, | ||
| rootCount: lane.rootJobIds.length, | ||
| }) | ||
| ) | ||
| ); | ||
| } | ||
| async function loadWgslAsset(assetUrl, options = {}) { | ||
@@ -199,2 +230,4 @@ const { url = assetUrl, fetcher = globalThis.fetch } = options ?? {}; | ||
| dependencyCount: job.dependencies.length, | ||
| unresolvedDependencyCount: job.dependencies.length, | ||
| dependentCount: (dependentsById.get(job.id) ?? []).length, | ||
| root: job.dependencies.length === 0, | ||
@@ -204,2 +237,4 @@ }) | ||
| const priorityLanes = buildPriorityLanes(graphJobs); | ||
| return Object.freeze({ | ||
@@ -209,6 +244,8 @@ mode: "dag", | ||
| maxPriority, | ||
| jobIds: Object.freeze(graphJobs.map((job) => job.id)), | ||
| roots: Object.freeze(graphJobs.filter((job) => job.root).map((job) => job.id)), | ||
| topologicalOrder: Object.freeze(topo), | ||
| priorityLanes, | ||
| jobs: Object.freeze(graphJobs), | ||
| }); | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
117695
8.32%883
16.95%143
9.16%4
-20%