Last active
July 16, 2017 18:23
-
-
Save qnoid/9cbc23c7a114e0506f93eae580828181 to your computer and use it in GitHub Desktop.
Train of thought on how to design the idea of running a series of processes, lazily created, stopping execution early on first error.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 1 | |
let process = Process() | |
process.launch() | |
process.waitUntilExit() | |
// 2. | |
let process = Process() | |
let queue = DispatchQueue() | |
queue.async { | |
process.launch() | |
process.waitUntilExit() | |
completion(process.terminationStatus) | |
} | |
// 3. | |
let queue = DispatchQueue() | |
func async(process:Process, failure:(_ code:Int)->Void) { | |
queue.async { | |
process.launch() | |
process.waitUntilExit() | |
guard process.terminationStatus == 0 else { | |
// error | |
failure(process.terminationStatus) | |
} | |
} | |
} | |
let process = Process() | |
async(process) { code in | |
} | |
// 4. | |
typealias ErrorFunction = (failure: (_ code: Int) -> Void) | |
let queue = DispatchQueue() | |
func async(process:Process) -> ErrorFunction { | |
let error = func error(failure: (_ code:Int) -> Void) | |
queue.async { | |
process.launch() | |
process.waitUntilExit() | |
guard process.terminationStatus == 0 else { | |
// error | |
failure(process.terminationStatus) | |
} | |
} | |
return error | |
} | |
let process = Process() | |
async(process)({ code in | |
}) | |
// 5. | |
typealias ErrorFunction = (failure: (_ code: Int) -> Void) | |
let queue = DispatchQueue() | |
func execute(process: Process) -> () -> Process { | |
return { | |
process.launch() | |
process.waitUntilExit() | |
return process | |
} | |
} | |
func async(process:Process) -> ErrorFunction { | |
let run = execute(process:process) | |
let error = func error(failure: (_ code: Int) -> Void) | |
queue.async { | |
let process = run() | |
guard process.terminationStatus == 0 else { | |
// error | |
failure(process.terminationStatus) | |
} | |
} | |
return error | |
} | |
// 6. | |
typealias Execute = () throws -> Void | |
struct ForwardExecution { | |
let execute: Execute | |
func run() throws -> Void { | |
execute() | |
} | |
func then(_ execute: Execute) -> ForwardExecution { | |
let before = this.execute | |
return ForwardExecution { | |
before() | |
execute() | |
} | |
} | |
func error(failure: (_ error: Error) -> Void) -> DispatchWorkItem { | |
return DispatchWorkItem { | |
try { | |
run() | |
} catch error { | |
failure(error) | |
} | |
} | |
} | |
} | |
func makeExecute(_ processProvider: @autoclosure () -> Process) -> Execution { | |
return { | |
let process = processProvider() | |
process.launch() | |
process.waitUntilExit() | |
guard process.terminationStatus == 0 else { | |
// error | |
throw NSError(code: process.terminationStatus) | |
} | |
} | |
} | |
let execute = makeExecute( Process() ) | |
let forwardExecution = ForwardExecution( execute: execute ) | |
let dispatchWorkItem = forwardExecution.then( makeExecute( Process() ) ).error { error in | |
} | |
let queue = DispatchQueue() | |
queue.async(dispatchWorkItem) |
Would there be much to gain by making each Process launch a distinct DispatchWorkItem to schedule on a queue?
How would that affect the design?
Here is my psuedo-python psuedo-code with what I was talking about, assuming that the list of tasks to run after each poll for changes doesn't change. I think this is similar to what you had initially? What does this approach lack?
// Global here for simplicity, only written to by main thread, etc...
running = True
// Assuming the list of procs/scripts to run doesn't change
procs = [build, test, deploy, ...]
def pollHasChanged(repo):
... check for changes, sleep thread, etc...
def pollForChanges(repo):
changed = False
while not changed:
changed = pollHasChanged(repo)
def monitorRepo(repo, procList):
global running
while running:
pollForChanges(repo)
for proc in procList:
// Assume execute returns truthy on failure
if proc.execute():
error = proc.stderr.read()
log(error)
break
// the 'api' (haha)
def monitor(repo):
// this could use a thread pool or similar
monitorThread = Thread.run(target=monitor, args=(procList, ))
monitorThread.start()
return monitorThread
monitor(someRepoHandle)
monitor(someOtherRepoHandle)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Written on a text editor on an iPhone. Might be some compilation issues.