Files: cb65ff6ecfa7aaaa00e83099fc9a0b3415ba32c1 / index.js
4345 bytesRaw
1 | const Kernel = require('./kernel.js') |
2 | const Scheduler = require('./scheduler.js') |
3 | const DFSchecker = require('./dfsChecker.js') |
4 | const CreationService = require('./creationService.js') |
5 | |
6 | const CREATION_ID = 0 |
7 | |
8 | module.exports = class Hypervisor { |
9 | /** |
10 | * The Hypervisor manages the container instances by instantiating them and |
11 | * destorying them when possible. It also facilitates localating Containers |
12 | * @param {Graph} dag an instance of [ipfs.dag](https://github.com/ipfs/interface-ipfs-core/tree/master/API/dag#dag-api) |
13 | * @param {object} state - the starting state |
14 | */ |
15 | constructor (tree) { |
16 | this.tree = tree |
17 | this.scheduler = new Scheduler() |
18 | this._containerTypes = {} |
19 | this._nodesToCheck = new Set() |
20 | |
21 | this.creationService = new CreationService({ |
22 | hypervisor: this |
23 | }) |
24 | this.scheduler.systemServices.set(CREATION_ID, this.creationService) |
25 | this.pinnedIds = new Set() |
26 | } |
27 | |
28 | /** |
29 | * add a potaintail node in the state graph to check for garbage collection |
30 | * @param {string} id |
31 | */ |
32 | addNodeToCheck (id) { |
33 | this._nodesToCheck.add(id) |
34 | } |
35 | |
36 | /** |
37 | * given a port, this finds the corridsponeding endpoint port of the channel |
38 | * @param {object} port |
39 | * @returns {Promise} |
40 | */ |
41 | async getDestPort (port) { |
42 | if (port.destPort) { |
43 | return port.destPort |
44 | } else { |
45 | const instance = await this.scheduler.getInstance(port.destId) |
46 | let containerState |
47 | if (instance) { |
48 | containerState = instance.state |
49 | } else { |
50 | let {value} = await this.tree.get(port.destId, true) |
51 | containerState = value |
52 | } |
53 | return this.tree.graph.get(containerState, `ports/${port.destName}`) |
54 | } |
55 | } |
56 | |
57 | async send (port, message) { |
58 | const id = port.destId |
59 | if (id !== undefined) { |
60 | const instance = await this.getInstance(id) |
61 | return instance.queue(port, message) |
62 | } else { |
63 | // port is unbound |
64 | port.destPort.messages.push(message) |
65 | } |
66 | } |
67 | |
68 | // loads an instance of a container from the state |
69 | async _loadInstance (id) { |
70 | const state = await this.tree.get(id, true) |
71 | const container = this._containerTypes[state.value.type] |
72 | |
73 | // create a new kernel instance |
74 | const kernel = new Kernel({ |
75 | hypervisor: this, |
76 | state: state.value, |
77 | node: state.node, |
78 | code: state.value.code, |
79 | container: container, |
80 | id: id |
81 | }) |
82 | |
83 | // save the newly created instance |
84 | this.scheduler.update(kernel) |
85 | return kernel |
86 | } |
87 | |
88 | /** |
89 | * gets an existsing container instances |
90 | * @param {string} id - the containers ID |
91 | * @returns {Promise} |
92 | */ |
93 | async getInstance (id) { |
94 | let instance = this.scheduler.getInstance(id) |
95 | if (instance) { |
96 | return instance |
97 | } else { |
98 | const resolve = this.scheduler.lock(id) |
99 | const instance = await this._loadInstance(id) |
100 | await instance.startup() |
101 | resolve(instance) |
102 | return instance |
103 | } |
104 | } |
105 | |
106 | getResponsePort (message) { |
107 | if (message.responsePort) { |
108 | return message.responsePort.destPort |
109 | } else { |
110 | const [portRef1, portRef2] = this.createChannel() |
111 | message.responsePort = portRef2 |
112 | return portRef1 |
113 | } |
114 | } |
115 | |
116 | createChannel () { |
117 | const port1 = { |
118 | messages: [] |
119 | } |
120 | |
121 | const port2 = { |
122 | messages: [], |
123 | destPort: port1 |
124 | } |
125 | |
126 | port1.destPort = port2 |
127 | return [port1, port2] |
128 | } |
129 | |
130 | /** |
131 | * creates a state root starting from a given container and a given number of |
132 | * ticks |
133 | * @param {Number} ticks the number of ticks at which to create the state root |
134 | * @returns {Promise} |
135 | */ |
136 | async createStateRoot (ticks) { |
137 | await this.scheduler.wait(ticks) |
138 | |
139 | const unlinked = await DFSchecker(this.tree, this._nodesToCheck, (id) => { |
140 | return this.pinnedIds.has(id) |
141 | }) |
142 | for (const id of unlinked) { |
143 | await this.tree.delete(id) |
144 | } |
145 | |
146 | return this.tree.flush() |
147 | } |
148 | |
149 | /** |
150 | * regirsters a container with the hypervisor |
151 | * @param {Class} Constructor - a Class for instantiating the container |
152 | * @param {*} args - any args that the contructor takes |
153 | * @param {interger} typeId - the container's type identification ID |
154 | */ |
155 | registerContainer (Constructor, args, typeId = Constructor.typeId) { |
156 | this._containerTypes[typeId] = { |
157 | Constructor: Constructor, |
158 | args: args |
159 | } |
160 | } |
161 | |
162 | pin (instance) { |
163 | this.pinnedIds.add(instance.id) |
164 | } |
165 | } |
166 |
Built with git-ssb-web