Files: cd25165f33c14d34c9ec8f487af17959f29ed06d / index.js
4531 bytesRaw
1 | const Graph = require('ipld-graph-builder') |
2 | const Message = require('primea-message') |
3 | const Kernel = require('./kernel.js') |
4 | const Scheduler = require('./scheduler.js') |
5 | const DFSchecker = require('./dfsChecker.js') |
6 | |
7 | const ROOT_ID = 'zdpuAm6aTdLVMUuiZypxkwtA7sKm7BWERy8MPbaCrFsmiyzxr' |
8 | |
9 | module.exports = class Hypervisor { |
10 | /** |
11 | * The Hypervisor manages the container instances by instantiating them and |
12 | * destorying them when possible. It also facilitates localating Containers |
13 | * @param {Graph} dag an instance of [ipfs.dag](https://github.com/ipfs/interface-ipfs-core/tree/master/API/dag#dag-api) |
14 | * @param {object} state - the starting state |
15 | */ |
16 | constructor (dag, state = {}) { |
17 | this.graph = new Graph(dag) |
18 | this.scheduler = new Scheduler() |
19 | this.state = state |
20 | this._containerTypes = {} |
21 | this._nodesToCheck = new Set() |
22 | } |
23 | |
24 | /** |
25 | * add a potaintail node in the state graph to check for garbage collection |
26 | * @param {string} id |
27 | */ |
28 | addNodeToCheck (id) { |
29 | this._nodesToCheck.add(id) |
30 | } |
31 | |
32 | /** |
33 | * given a port, this finds the corridsponeding endpoint port of the channel |
34 | * @param {object} port |
35 | * @returns {Promise} |
36 | */ |
37 | getDestPort (port) { |
38 | if (port.destPort) { |
39 | return port.destPort |
40 | } else { |
41 | return this.graph.get(this.state, `${port.destId}/ports/${port.destName}`) |
42 | } |
43 | } |
44 | |
45 | async send (port, message) { |
46 | if (port.destId) { |
47 | const id = port.destId |
48 | const instance = await this.getInstance(id) |
49 | return instance.queue(port.destName, message) |
50 | } else { |
51 | // port is unbound |
52 | port.destPort.messages.push(message) |
53 | } |
54 | } |
55 | |
56 | // loads an instance of a container from the state |
57 | async _loadInstance (id) { |
58 | const state = await this.graph.get(this.state, id) |
59 | const container = this._containerTypes[state.type] |
60 | |
61 | // create a new kernel instance |
62 | const kernel = new Kernel({ |
63 | hypervisor: this, |
64 | state: state, |
65 | container: container, |
66 | id: id |
67 | }) |
68 | |
69 | // save the newly created instance |
70 | this.scheduler.update(kernel) |
71 | return kernel |
72 | } |
73 | |
74 | // get a hash from a POJO |
75 | _getHashFromObj (obj) { |
76 | return this.graph.flush(obj).then(obj => obj['/']) |
77 | } |
78 | |
79 | /** |
80 | * gets an existsing container instances |
81 | * @param {string} id - the containers ID |
82 | * @returns {Promise} |
83 | */ |
84 | async getInstance (id) { |
85 | let instance = this.scheduler.getInstance(id) |
86 | if (instance) { |
87 | return instance |
88 | } else { |
89 | const resolve = this.scheduler.getLock(id) |
90 | const instance = await this._loadInstance(id) |
91 | resolve(instance) |
92 | return instance |
93 | } |
94 | } |
95 | |
96 | /** |
97 | * creates an new container instances and save it in the state |
98 | * @param {string} type - the type of container to create |
99 | * @param {*} code |
100 | * @param {array} entryPorts |
101 | * @param {object} id |
102 | * @param {object} id.nonce |
103 | * @param {object} id.parent |
104 | * @returns {Promise} |
105 | */ |
106 | async createInstance (type, message = new Message(), id = {nonce: 0, parent: null}) { |
107 | // create a lock to prevent the scheduler from reloving waits before the |
108 | // new container is loaded |
109 | const resolve = this.scheduler.getLock(id) |
110 | const idHash = await this._getHashFromObj(id) |
111 | const code = message.data.byteLength ? message.data : undefined |
112 | const state = { |
113 | nonce: [0], |
114 | ports: {}, |
115 | type: type, |
116 | code: code |
117 | } |
118 | |
119 | // save the container in the state |
120 | await this.graph.set(this.state, idHash, state) |
121 | // create the container instance |
122 | const instance = await this._loadInstance(idHash) |
123 | resolve(instance) |
124 | // send the intialization message |
125 | await instance.initialize(message) |
126 | |
127 | return instance |
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 | const unlinked = await DFSchecker(this.graph, this.state, ROOT_ID, this._nodesToCheck) |
139 | unlinked.forEach(id => { |
140 | delete this.state[id] |
141 | }) |
142 | return this.graph.flush(this.state) |
143 | } |
144 | |
145 | /** |
146 | * regirsters a container with the hypervisor |
147 | * @param {String} type - the name of the type |
148 | * @param {Class} Constructor - a Class for instantiating the container |
149 | * @param {*} args - any args that the contructor takes |
150 | */ |
151 | registerContainer (type, Constructor, args) { |
152 | this._containerTypes[type] = { |
153 | Constructor: Constructor, |
154 | args: args |
155 | } |
156 | } |
157 | } |
158 |
Built with git-ssb-web