Files: 5915f4b12de689ba1985efe90c4181b4e774977c / index.js
4246 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 | // loads an instance of a container from the state |
46 | async _loadInstance (id) { |
47 | const state = await this.graph.get(this.state, id) |
48 | const container = this._containerTypes[state.type] |
49 | |
50 | // create a new kernel instance |
51 | const kernel = new Kernel({ |
52 | hypervisor: this, |
53 | state: state, |
54 | container: container, |
55 | id: id |
56 | }) |
57 | |
58 | // save the newly created instance |
59 | this.scheduler.update(kernel) |
60 | return kernel |
61 | } |
62 | |
63 | // get a hash from a POJO |
64 | async _getHashFromObj (obj) { |
65 | return (await this.graph.flush(obj))['/'] |
66 | } |
67 | |
68 | /** |
69 | * gets an existsing container instances |
70 | * @param {string} id - the containers ID |
71 | * @returns {Promise} |
72 | */ |
73 | async getInstance (id) { |
74 | let instance = this.scheduler.getInstance(id) |
75 | if (instance) { |
76 | return instance |
77 | } else { |
78 | const resolve = this.scheduler.getLock(id) |
79 | const instance = await this._loadInstance(id) |
80 | resolve(instance) |
81 | return instance |
82 | } |
83 | } |
84 | |
85 | /** |
86 | * creates an new container instances and save it in the state |
87 | * @param {string} type - the type of container to create |
88 | * @param {*} code |
89 | * @param {array} entryPorts |
90 | * @param {object} id |
91 | * @param {object} id.nonce |
92 | * @param {object} id.parent |
93 | * @returns {Promise} |
94 | */ |
95 | async createInstance (type, message = new Message(), id = {nonce: 0, parent: null}) { |
96 | // create a lock to prevent the scheduler from reloving waits before the |
97 | // new container is loaded |
98 | const resolve = this.scheduler.getLock(id) |
99 | const idHash = await this._getHashFromObj(id) |
100 | const code = message.data.byteLength ? message.data : undefined |
101 | const state = { |
102 | nonce: [0], |
103 | ports: {}, |
104 | type: type, |
105 | code: code |
106 | } |
107 | |
108 | // save the container in the state |
109 | await this.graph.set(this.state, idHash, state) |
110 | // create the container instance |
111 | const instance = await this._loadInstance(idHash) |
112 | |
113 | resolve(instance) |
114 | // send the intialization message |
115 | instance.initialize(message) |
116 | |
117 | return instance |
118 | } |
119 | |
120 | /** |
121 | * creates a state root starting from a given container and a given number of |
122 | * ticks |
123 | * @param {Number} ticks the number of ticks at which to create the state root |
124 | * @returns {Promise} |
125 | */ |
126 | async createStateRoot (ticks) { |
127 | await this.scheduler.wait(ticks) |
128 | const unlinked = await DFSchecker(this.graph, this.state, ROOT_ID, this._nodesToCheck) |
129 | unlinked.forEach(id => { |
130 | delete this.state[id] |
131 | }) |
132 | return this.graph.flush(this.state) |
133 | } |
134 | |
135 | /** |
136 | * regirsters a container with the hypervisor |
137 | * @param {String} type - the name of the type |
138 | * @param {Class} Constructor - a Class for instantiating the container |
139 | * @param {*} args - any args that the contructor takes |
140 | */ |
141 | registerContainer (type, Constructor, args) { |
142 | this._containerTypes[type] = { |
143 | Constructor: Constructor, |
144 | args: args |
145 | } |
146 | } |
147 | } |
148 |
Built with git-ssb-web