Skip to content

Instantly share code, notes, and snippets.

@qnoid
Last active July 16, 2017 18:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save qnoid/9cbc23c7a114e0506f93eae580828181 to your computer and use it in GitHub Desktop.
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.
// 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)
@qnoid
Copy link
Author

qnoid commented Jul 14, 2017

Written on a text editor on an iPhone. Might be some compilation issues.

@qnoid
Copy link
Author

qnoid commented Jul 14, 2017

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?

@callumenator
Copy link

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