diff --git a/ferus.nimble b/ferus.nimble index 8f8ab18..f770db7 100644 --- a/ferus.nimble +++ b/ferus.nimble @@ -13,12 +13,12 @@ bin = @["ferus", "ferus_process"] requires "nim >= 2.0.2" requires "ferusgfx >= 1.2.1" requires "colored_logger >= 0.1.0" -requires "stylus#master" +requires "stylus >= 0.1.3" requires "https://github.com/ferus-web/sanchar >= 2.0.2" requires "https://git.sr.ht/~bptato/chame >= 1.0.1" requires "seccomp >= 0.2.1" requires "simdutf >= 5.5.0" -requires "https://github.com/ferus-web/bali >= 0.6.3" +requires "https://github.com/ferus-web/bali#master" requires "results >= 0.5.0" requires "pretty >= 0.1.0" requires "jsony >= 1.1.5" diff --git a/src/bindings/yoga.nim b/src/bindings/yoga.nim index 81c1109..4ac8080 100644 --- a/src/bindings/yoga.nim +++ b/src/bindings/yoga.nim @@ -18,7 +18,7 @@ static: echo pwd {.passC: "-I" & gorge("pwd") & "/install/include".} -{.passL: "-L" & gorge("pwd") & "/install/lib64 -lyogacore".} +{.passL: "-L" & gorge("pwd") & "/install/lib -lyogacore".} {.push header: "".} type diff --git a/src/components/ipc/shared.nim b/src/components/ipc/shared.nim index 225cae6..4f448e7 100755 --- a/src/components/ipc/shared.nim +++ b/src/components/ipc/shared.nim @@ -395,6 +395,8 @@ proc magicFromStr*(s: string): Option[FerusMagic] = return some feJSTakeDocument of "feRendererExit": return some feRendererExit + of "feGoodbye": + return some feGoodbye else: warn "magicFromStr(" & s & "): no such magic string found." diff --git a/src/components/js/process.nim b/src/components/js/process.nim index 900c620..fff6b3f 100644 --- a/src/components/js/process.nim +++ b/src/components/js/process.nim @@ -15,6 +15,7 @@ type JSProcess* = object ipc*: IPCClient parser*: Parser runtime*: Runtime + running*: bool = true document*: HTMLDocument @@ -72,6 +73,14 @@ proc talk(js: var JSProcess, process: FerusProcess) = debug "Got document for this tab - passing it to JS land." js.document = packet.document + of feGoodbye: + info "js: got goodbye packet, cleaning up." + # TODO: make it so that we always respond to goodbye(s), even when in an unreachable/expensive VM loop + # there's two ways to do this: + # a) either add a way for a hook function to constantly monitor for this packet (bad performance) + # b) shift the VM to another thread (good performance but harder to work with) + js.runtime.vm.halt = true + js.running = false else: discard @@ -87,5 +96,5 @@ proc jsProcessLogic*(client: var IPCClient, process: FerusProcess) {.inline.} = else: setLogFilter(lvlNone) - while true: + while js.running: js.talk(process) diff --git a/src/components/master/master.nim b/src/components/master/master.nim index 10f93b6..78477d9 100644 --- a/src/components/master/master.nim +++ b/src/components/master/master.nim @@ -1,3 +1,7 @@ +## The IPC "master". This is just an abstraction over the IPC server. +## It essentially allows you to do anything with your own group of processes (renderer, JS runtime, CSS/HTML parsers, etc.) +## This includes summoning them, telling them to do a task, telling them to exit, et cetera. + import std/ [os, logging, osproc, strutils, options, base64, net, sets, terminal, posix, tables] @@ -25,6 +29,8 @@ type MasterProcess* = ref object server*: IPCServer urls*: Table[uint, URL] + alive*: bool = true + proc initialize*(master: MasterProcess) {.inline.} = master.server.add(FerusGroup()) # TODO: multi-tab support, although we could just keep adding more FerusGroup(s) and it should *theoretically* scale @@ -515,8 +521,20 @@ proc packetHandler*( master.server.reportBadMessage( process, "Non-renderer process sent `feRendererExit` opcode", High ) + return + + info "The renderer has shut down. Beginning cleanup." + + # Here, we start sending "goodbye" packets to all processes. + # We're essentially telling them, "Hey, start cleaning up and die." + for i, group in master.server.groups: + for process in group: + debug "Telling PID " & $process.pid & " (group " & $i & "'s " & $process.kind & + ") to exit" + master.server.send(process.socket, GoodbyePacket()) - debug "The renderer has shut down." + info "Cleanup completed. Adios!" + master.alive = false else: warn "Unhandled IPC protocol magic: " & $kind return diff --git a/src/components/network/process.nim b/src/components/network/process.nim index 574a42c..0b8053b 100644 --- a/src/components/network/process.nim +++ b/src/components/network/process.nim @@ -162,6 +162,17 @@ proc talk(client: FerusNetworkClient, process: FerusProcess) {.inline.} = info "network: Created WebSocket connection to address " & $openReq.address & " successfully!" client.ipc.send(NetworkWebSocketCreationResult(error: none(string))) + of feGoodbye: + info "network: received goodbye packet." + + info "network: closing all websockets" + for ws in client.websockets: + ws.handle.close() + + info "network: closing cURL handle" + curl.close() + + client.running = false else: discard @@ -170,6 +181,6 @@ proc networkProcessLogic*(client: var IPCClient, process: FerusProcess) {.inline var client = FerusNetworkClient(ipc: client) client.ipc.setState(Idling) - while true: + while client.running: client.talk(process) client.tickAllConnections() diff --git a/src/components/network/types.nim b/src/components/network/types.nim index 2c2849f..0b7495e 100644 --- a/src/components/network/types.nim +++ b/src/components/network/types.nim @@ -10,3 +10,4 @@ type FerusNetworkClient* = ref object ipc*: IPCClient websockets*: seq[WebSocketConnection] ## All open WebSocket instances + running*: bool = true diff --git a/src/components/parsers/html/process.nim b/src/components/parsers/html/process.nim index 5be5250..f7c550c 100644 --- a/src/components/parsers/html/process.nim +++ b/src/components/parsers/html/process.nim @@ -24,7 +24,13 @@ proc htmlParse*(oparsingData: Option[ParseHTMLPacket]): HTMLParseResult = HTMLParseResult(document: some(document.parseHTMLDocument())) -proc talk(client: var IPCClient, process: FerusProcess) {.inline.} = +type HTMLParserData* = object + running*: bool = true + documentsParsed*: uint64 + +proc talk( + client: var IPCClient, state: var HTMLParserData, process: FerusProcess +) {.inline.} = var count: cint discard nix.ioctl(client.socket.getFd().cint, nix.FIONREAD, addr(count)) @@ -53,13 +59,21 @@ proc talk(client: var IPCClient, process: FerusProcess) {.inline.} = client.send(data) client.setState(Idling) + + inc state.documentsParsed + of feGoodbye: + info "html: got goodbye packet, exiting." + info "html: we parsed " & $state.documentsParsed & + " documents throughout this process's lifetime" + state.running = false else: discard proc htmlParserProcessLogic*(client: var IPCClient, process: FerusProcess) {.inline.} = info "Entering HTML parser process logic." + var data = HTMLParserData() client.setState(Idling) client.poll() - while true: - client.talk(process) + while data.running: + client.talk(data, process) diff --git a/src/ferus.nim b/src/ferus.nim index 8cf8207..f3dcb71 100644 --- a/src/ferus.nim +++ b/src/ferus.nim @@ -75,7 +75,7 @@ proc main() {.inline.} = tab1.load() tabs.add(tab1.move()) - while true: + while tabs[0].master.alive: master.poll() for i, _ in tabs: tabs[i].heartbeat()