diff --git a/constants.js b/constants.js
index b4a381084..4b2ec9ede 100644
--- a/constants.js
+++ b/constants.js
@@ -70,7 +70,7 @@ exports.WAKEUP = toPath(new URL(BIN + WAKEUP_EXEC, swapURL))
exports.RUNTIME = toPath(new URL(BIN + RUNTIME_EXEC, swapURL))
exports.DESKTOP_RUNTIME = toPath(new URL(BIN + DESKTOP_EXEC, swapURL))
-exports.BARE_RESTART_EXIT_CODE = 75
+exports.RESTART_EXIT_CODE = 75
function electronModuleURL () {
const u = pathToFileURL(process.execPath)
diff --git a/decal.html b/decal.html
index 998c392ba..28c91324a 100644
--- a/decal.html
+++ b/decal.html
@@ -546,8 +546,7 @@
}
if (name === 'action') {
const cta = this.shadowRoot.querySelector('#cta')
- const code = (value === 'reload') ? 64 : 0
- const handler = (value === 'quit' || value === 'reload') ?
+ const handler = (value === 'quit') ?
() => Pear.exit(code) :
() => {}
cta.addEventListener('click', handler)
diff --git a/electron-main.js b/electron-main.js
index 4455715ad..bbd7200b9 100644
--- a/electron-main.js
+++ b/electron-main.js
@@ -49,10 +49,8 @@ async function electronMain (cmd) {
electron.ipcMain.on('send-to', (e, id, channel, message) => { electron.webContents.fromId(id)?.send(channel, message) })
- const app = await gui.app()
- app.unloading().then(async () => {
- await app.close()
- }) // note: would be unhandled rejection on failure, but should never fail
+ await gui.app()
+
}
function configureElectron () {
diff --git a/gui/gui.js b/gui/gui.js
index a0ffd5d00..261d1d782 100644
--- a/gui/gui.js
+++ b/gui/gui.js
@@ -374,16 +374,35 @@ class App {
state = null
ipc = null
id = null
+ session = null
handle = null
closing = null
closed = false
appReady = false
static root = unixPathResolve(resolve(__dirname, '..'))
- constructor (state, ipc) {
+ constructor (state, ipc, gui) {
this.state = state
this.ipc = ipc
+ this.gui = gui
this.contextMenu = null
+
+ this.ipc.messages({ type: 'pear/reload' }).once('data', async () => {
+ const lockWait = ipc.waitForLock()
+ await this.ipc.close()
+ console.log('ipc closed')
+ console.log('waiting for lock')
+ await lockWait
+ console.log('sidecar closed')
+ const id = 'todo'
+ this.state.id = id
+ this.session.setUserAgent(`Pear ${id}`)
+ console.log('now trigger app start and update electron + useragent with new startid')
+ console.log('then tell views to location.reload')
+ //location.reload()
+ })
+
+
electron.app.on('browser-window-focus', () => { this.menu.devtoolsReloaderUnlisten() })
electron.app.on('child-process-gone', (e, details) => {
@@ -436,6 +455,7 @@ class App {
})
})
this.menu = new Menu(this)
+
}
async report ({ err }) {
@@ -577,7 +597,7 @@ class App {
}
}
},
- afterNativeWindowClose: () => this.close(),
+ afterClose: () => this.close(),
afterNativeViewCreated: devtools && ((app) => {
if (trace) return
app.view.webContents.openDevTools({ mode: 'detach' })
@@ -612,7 +632,9 @@ class App {
}
})
this.id = ctrl.id
+ this.session = ctrl.session
await this.starting
+ this.unloading() // note: would be unhandled rejection on failure, but should never fail
} catch (err) {
await this.report({ err })
this.close()
@@ -626,7 +648,12 @@ class App {
return { fork, length, key: key ? key.toString('hex') : null }
}
- unloading () { return this.ipc.unloading() }
+ async unloading () {
+ const action = await this.ipc.unloading()
+ for (const ctrl of PearGUI.ctrls()) ctrl.unload(action)
+ if (action.type === 'teardown') await this.ipc.close()
+ return action
+ }
close (maxWait = 5500) {
if (this.closing) return this.closing
@@ -862,16 +889,17 @@ class GuiCtrl {
async close () {
if (this.closed) return true
+ console.log('this.unload', this.unload)
if (this.unload) {
this.unload({ type: 'close' })
await this.unloader
}
- let closer = null
- if (this.win) {
- closer = once(this.win, 'closed')
- this.win.close()
- }
- await closer
+ // let closer = null
+ // if (this.win) {
+ // closer = once(this.win, 'closed')
+ // this.win.close()
+ // }
+ // await closer
this.constructor[kMap].delete(this.id)
this.id = null
this.win = null
@@ -917,8 +945,11 @@ class GuiCtrl {
async unloading () {
if (!this.#unloading) this.#unloading = this._unloading()
try {
- return await this.#unloading
+ const xxx = await this.#unloading
+ console.log('???', xxx)
+ return xxx
} finally {
+ console.log('UNLDD')
this.#unloading = null
}
}
@@ -926,12 +957,13 @@ class GuiCtrl {
async _unloading () {
const { webContents } = (this.view || this.win)
const until = new Promise((resolve) => { this.unload = resolve })
- webContents.once('will-navigate', (e, url) => {
+ const willNavListener = (e, url) => {
if (!url.startsWith(this.sidecar)) return // handled by the other will-navigate handler
e.preventDefault()
const type = (!e.frame || e.frame.url === url) ? 'reload' : 'nav'
this.unload({ type, url })
- })
+ }
+ webContents.once('will-navigate', willNavListener)
const closeListener = (e) => {
e.preventDefault()
@@ -940,16 +972,20 @@ class GuiCtrl {
}
}
if (this.win) this.win.once('close', closeListener)
+ webContents.removeListener('will-navigate', willNavListener)
this.unloader = new Promise((resolve) => { this.unloaded = resolve })
const action = await until
+ console.log('ACTION', action)
if (this.win) this.win.removeListener('close', closeListener)
this.unload = null
return action
}
- completeUnload (action) {
+ async completeUnload (action) {
this.unloaded()
- if (action.type === 'close') this.close()
+ console.log('complete unload', action)
+ // await this.close()
+ // if (action.type === 'close') this.close()
}
setWindowButtonPosition (point) {
@@ -1025,11 +1061,8 @@ class Window extends GuiCtrl {
this.win.on('close', () => {
this.closing = true
})
-
this.win.on('closed', () => {
- if (typeof options.afterNativeWindowClose === 'function') {
- options.afterNativeWindowClose(this)
- }
+ if (typeof options.afterClose === 'function') options.afterClose(this)
if (this.win) {
if (this.opening) this.opening = false
this.win.closed = true
@@ -1408,7 +1441,10 @@ class PearGUI extends ReadyResource {
})
this.worker = new Worker()
this.pipes = new Freelist()
- this.ipc.once('close', () => this.close())
+ this.ipc.once('close', () => {
+ console.log('IPC CLOSED')
+ // this.close()
+ })
electron.ipcMain.on('exit', (e, code) => { process.exit(code) })
@@ -1439,6 +1475,9 @@ class PearGUI extends ReadyResource {
messages.on('end', () => event.reply('messages', null))
})
+ electron.ipcMain.handle('waitForLock', () => this.ipc.waitForLock())
+ electron.ipcMain.handle('close', () => this._close())
+
electron.ipcMain.handle('getMediaAccessStatus', (evt, ...args) => this.getMediaAccessStatus(...args))
electron.ipcMain.handle('askForMediaAccess', (evt, ...args) => this.askForMediaAccess(...args))
electron.ipcMain.handle('desktopSources', (evt, ...args) => this.desktopSources(...args))
@@ -1446,7 +1485,7 @@ class PearGUI extends ReadyResource {
electron.ipcMain.handle('ctrl', (evt, ...args) => this.ctrl(...args))
electron.ipcMain.handle('parent', (evt, ...args) => this.parent(...args))
electron.ipcMain.handle('open', (evt, ...args) => this.open(...args))
- electron.ipcMain.handle('close', (evt, ...args) => this.guiClose(...args))
+ electron.ipcMain.handle('guiClose', (evt, ...args) => this.guiClose(...args))
electron.ipcMain.handle('show', (evt, ...args) => this.show(...args))
electron.ipcMain.handle('hide ', (evt, ...args) => this.hide(...args))
electron.ipcMain.handle('minimize', (evt, ...args) => this.minimize(...args))
@@ -1529,8 +1568,8 @@ class PearGUI extends ReadyResource {
}
async app () {
- const app = new App(this.state, this.ipc)
- this.once('close', async () => { app.quit() })
+ const app = new App(this.state, this.ipc, this)
+ // this.once('close', async () => { app.quit() })
await app.start()
return app
}
@@ -1540,7 +1579,9 @@ class PearGUI extends ReadyResource {
}
async _close () {
+ console.log('calling ipc close')
await this.ipc.close()
+ console.log('ipc close done')
}
static async ctrl (type, entry, { state, parentId = 0, ua, sessname = null, appkin }, options = {}, openOptions = {}) {
@@ -1617,7 +1658,7 @@ class PearGUI extends ReadyResource {
}
}
- async askForMediaAccess ({ id, media }) {
+ async askForMediaAccess ({ media }) {
if (isLinux || isWindows) return false
if (media === 'screen') {
return electron.systemPreferences.getMediaAccessStatus(media)
diff --git a/gui/preload.js b/gui/preload.js
index c717ebcd9..df77c14b7 100644
--- a/gui/preload.js
+++ b/gui/preload.js
@@ -18,14 +18,19 @@ module.exports = class PearGUI extends ReadyResource {
})
const onteardown = async (fn) => {
- if (state.isDecal) return
await this.ready()
const action = await this.ipc.unloading({ id }) // only resolves when unloading occurs
await fn()
await this.ipc.completeUnload({ id, action })
+ if (action.type === 'teardown') {
+ console.log('onteardown ipc close')
+ await this.ipc.closeIPC()
+ console.log('onteardown ipc closed')
+ }
if (action.type === 'reload') location.reload()
else if (action.type === 'nav') location.href = action.url
}
+ const gui = this
API = class extends API {
constructor (ipc, state, onteardown) {
super(ipc, state, onteardown)
@@ -45,6 +50,18 @@ module.exports = class PearGUI extends ReadyResource {
desktopSources: (options = {}) => ipc.desktopSources(options)
}
+ // if (state.isDecal === false) ipc.messages({ type: 'pear/reload' }).once('data', async () => {
+ // const lockWait = ipc.waitForLock()
+ // await gui.close()
+ // console.log('ipc closed')
+ // console.log('waiting for lock')
+ // await lockWait
+ // console.log('sidecar closed')
+ // console.log('now wait trigger app start and update electron with new startid')
+ // console.log('ok so call location.reload')
+ // //location.reload()
+ // })
+
const kGuiCtrl = Symbol('gui:ctrl')
class Parent extends EventEmitter {
@@ -213,6 +230,11 @@ module.exports = class PearGUI extends ReadyResource {
this.View = View
}
+ reload = async function (opts) {
+ if (opts?.platform) return this._reload(opts)
+ location.reload()
+ }
+
exit = (code) => {
process.exitCode = code
electron.ipcRenderer.sendSync('exit', code)
@@ -220,6 +242,14 @@ module.exports = class PearGUI extends ReadyResource {
}
this.api = new API(this.ipc, state, onteardown)
}
+
+ _close () {
+
+ // NEED TO TELL PARENT PROCESS TO CLOSE ITS IPC AS WELL SOMEHOW
+
+ console.trace('PRELOAD _close')
+ return this.ipc.closeIPC()
+ }
}
class IPC {
@@ -230,7 +260,7 @@ class IPC {
ctrl (...args) { return electron.ipcRenderer.invoke('ctrl', ...args) }
parent (...args) { return electron.ipcRenderer.invoke('parent', ...args) }
open (...args) { return electron.ipcRenderer.invoke('open', ...args) }
- close (...args) { return electron.ipcRenderer.invoke('close', ...args) }
+ close (...args) { return electron.ipcRenderer.invoke('guiClose', ...args) }
show (...args) { return electron.ipcRenderer.invoke('show', ...args) }
hide (...args) { return electron.ipcRenderer.invoke('hide', ...args) }
minimize (...args) { return electron.ipcRenderer.invoke('minimize', ...args) }
@@ -307,14 +337,14 @@ class IPC {
stream.emit('error', new Error('Worker PipeError (from electron-main): ' + stack))
})
electron.ipcRenderer.on('workerClose', () => { stream.destroy() })
- stream.once('close', () => {
- electron.ipcRenderer.send('workerPipeClose', id)
- })
+ stream.once('close', () => { electron.ipcRenderer.send('workerPipeClose', id) })
electron.ipcRenderer.on('workerPipeData', (e, data) => { stream.push(data) })
return stream
}
+ waitForLock () { return electron.ipcRenderer.invoke('waitForLock') }
+ closeIPC () { return electron.ipcRenderer.invoke('close') }
ref () {}
unref () {}
@@ -340,4 +370,5 @@ class IPC {
electron.ipcRenderer.on('iteratePreferences', (e, data) => { stream.push(data) })
return stream
}
+
}
diff --git a/lib/api.js b/lib/api.js
index 772b5c55b..7b7654210 100644
--- a/lib/api.js
+++ b/lib/api.js
@@ -1,6 +1,6 @@
'use strict'
const Worker = require('./worker')
-const { BARE_RESTART_EXIT_CODE } = require('../constants')
+const { RESTART_EXIT_CODE } = require('../constants')
const noop = () => {}
const teardown = global.Bare ? require('./teardown') : noop
const program = global.Bare || global.process
@@ -97,22 +97,17 @@ class API {
versions = () => this.#reftrack(this.#ipc.versions())
restart = async (opts = {}) => {
- const restart = this.#reftrack(this.#ipc.restart({ ...opts, hard: true }))
+ const restart = this.#reftrack(this.#ipc.restart({ ...opts }))
return restart
}
- reload = async (opts = {}) => {
- if (!opts.platform) {
- // TODO: use Pear.shutdown when it lands instead
- if (this.#state.type === 'terminal') Bare.exit(BARE_RESTART_EXIT_CODE)
- else global.location.reload()
-
- return
- }
-
- return this.#reftrack(this.#ipc.restart({ ...opts, hard: false }))
+ async _reload (opts = {}) {
+ if (opts.platform) return this.#reftrack(this.#ipc.restart({ ...opts, reload: true }))
+ this.exit(RESTART_EXIT_CODE)
}
+ reload = (opts = {}) => this._reload(opts)
+
updates = (listener) => this.messages({ type: 'pear/updates' }, listener)
wakeups = (listener) => this.messages({ type: 'pear/wakeup' }, listener)
diff --git a/package.json b/package.json
index 033e55774..910610d75 100644
--- a/package.json
+++ b/package.json
@@ -94,7 +94,7 @@
"paparam": "^1.4.0",
"pear-changelog": "^1.0.1",
"pear-interface": "^1.0.0",
- "pear-ipc": "^1.0.17",
+ "pear-ipc": "^1.1.0",
"pear-link": "^2.0.1",
"pear-updater": "^3.1.0",
"protomux": "^3.6.0",
diff --git a/run/index.js b/run/index.js
index 55807ce3e..3e6f566cc 100644
--- a/run/index.js
+++ b/run/index.js
@@ -19,8 +19,7 @@ const {
} = require('../errors')
const parseLink = require('../lib/parse-link')
const teardown = require('../lib/teardown')
-const { PLATFORM_LOCK } = require('../constants')
-const fsext = require('fs-native-extensions')
+const { isWindows } = require('which-runtime')
module.exports = async function run ({ ipc, args, cmdArgs, link, storage, detached, flags, appArgs, indices }) {
const { drive, pathname } = parseLink(link)
@@ -116,15 +115,10 @@ module.exports = async function run ({ ipc, args, cmdArgs, link, storage, detach
global.Pear = pear
- const reloadSubscriber = ipc.messages({ type: 'pear/reload' })
- reloadSubscriber.on('data', async () => {
- ipc.stream.destroy()
-
- const fd = await new Promise((resolve, reject) => fs.open(PLATFORM_LOCK, 'r+', (err, fd) => err ? reject(err) : resolve(fd)))
- await fsext.waitForLock(fd)
- await new Promise((resolve, reject) => fs.close(fd, (err) => err ? reject(err) : resolve(fd)))
-
- await global.Pear.restart()
+ ipc.messages({ type: 'pear/reload' }).once('data', async () => {
+ await ipc.close()
+ await ipc.waitForLock()
+ await global.Pear.reload()
})
const protocol = new Module.Protocol({
@@ -143,6 +137,14 @@ module.exports = async function run ({ ipc, args, cmdArgs, link, storage, detach
return stream
}
+
+
+ ipc.messages({ type: 'pear/reload' }).once('data', async () => {
+ await ipc.close()
+ await ipc.waitForLock()
+ console.log('create a new ipc client? what for we do not need it')
+ })
+
args.unshift('--start-id=' + startId)
const detach = args.includes('--detach')
diff --git a/subsystems/sidecar/index.js b/subsystems/sidecar/index.js
index d0c0e39df..8a4b25b40 100644
--- a/subsystems/sidecar/index.js
+++ b/subsystems/sidecar/index.js
@@ -72,7 +72,6 @@ class Sidecar extends ReadyResource {
super()
this.bus = new Iambus()
this.version = CHECKOUT
-
this.updater = updater
if (this.updater) this.updater.on('update', (checkout) => this.updateNotify(checkout))
@@ -234,7 +233,8 @@ class Sidecar extends ReadyResource {
teardown () {
if (this.unload) {
- this.unload()
+ console.log('UNLOAD TYPE teardown')
+ this.unload({ type: 'teardown' })
return true
}
return false
@@ -286,7 +286,9 @@ class Sidecar extends ReadyResource {
if (this.decomissioned) return
if (this.hasClients) return
this.spindownt = setTimeout(async () => {
+ console.log('timeout complete', this.hasClients)
if (this.hasClients) return
+ console.log('spindownt calling close')
this.close().catch(console.error)
}, this.spindownms)
}
@@ -513,9 +515,9 @@ class Sidecar extends ReadyResource {
return metadata
}
- async restart ({ platform = false, hard = true } = {}, client) {
- if (this.verbose) console.log(`${hard ? 'Hard' : 'Soft'} restarting ${platform ? 'platform' : 'client'}`)
- if (platform === false) {
+ async restart ({ platform = false, reload = false } = {}, client) {
+ if (this.verbose) console.log(`${reload ? 'Reloading' : 'Restarting'} ${platform ? 'platform' : 'client'}`)
+ if (reload === false && platform === false) {
const { dir, cwd, cmdArgs, env } = client.userData.state
const appling = client.userData.state.appling
const opts = { cwd, env, detached: true, stdio: 'ignore' }
@@ -553,24 +555,19 @@ class Sidecar extends ReadyResource {
return
}
- if (!hard && this.hasClients) {
- const seen = new Set()
- for (const { userData: app } of this.clients) {
- if (!app.state || seen.has(app.state.id)) continue
- seen.add(app.state.id)
- app.message({ type: 'pear/reload' })
- }
- }
+ if (reload) for (const { userData: app } of this.clients) app.message({ type: 'pear/reload' })
const sidecarClosed = new Promise((resolve) => this.corestore.once('close', resolve))
let restarts = await this.#shutdown(client)
+
// ample time for any OS cleanup operations:
await new Promise((resolve) => setTimeout(resolve, 1500))
+
+ if (reload) return
+
// shutdown successful, reset death clock
this.deathClock()
- if (!hard) return
-
restarts = restarts.filter(({ run }) => run)
if (restarts.length === 0) return
if (this.verbose) console.log('Restarting', restarts.length, 'apps')
@@ -871,7 +868,6 @@ class Sidecar extends ReadyResource {
this.spindownms = 0
const restarts = this.closeClients()
-
this.spindownms = 0
this.#spindownCountdown()
await this.closing
@@ -879,26 +875,44 @@ class Sidecar extends ReadyResource {
}
async #close () {
+ console.log('#close')
+ console.log('pre appling close')
await this.applings.close()
+ console.log('post appling close')
clearTimeout(this.lazySwarmTimeout)
+ console.log('pre replicator leave')
if (this.replicator) await this.replicator.leave(this.swarm)
+ console.log('post replicator leave')
+ console.log('pre http close')
if (this.http) await this.http.close()
+ console.log('post http close')
+ console.log('pre http destroy')
if (this.swarm) await this.swarm.destroy()
+ console.log('posthttp destroy')
+ console.log('pre corestore close')
if (this.corestore) await this.corestore.close()
+ console.log('post corestore close')
if (this.verbose) console.log((isWindows ? '^' : '✔') + ' Sidecar closed')
}
async _close () {
+ console.log('_close')
if (this.decomissioned) return
this.decomissioned = true
// point of no return, death-march ensues
+
this.deathClock()
const closing = this.#close()
this.closeClients()
+ console.log('awaiting closing')
await closing
+ console.log('closing resolved')
+ console.log('awaiting ipc close')
await this.ipc.close()
+ console.log('resolved ipc close')
if (this.updater) {
+ console.log('awaiting update')
if (await this.updater.applyUpdate() !== null) {
if (this.verbose) console.log((isWindows ? '^' : '✔') + ' Applied update')
}