Skip to content

Instantly share code, notes, and snippets.

@seivan
Created September 24, 2018 08:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seivan/e559c4b03b6c6e329912b3fc86f2bdf0 to your computer and use it in GitHub Desktop.
Save seivan/e559c4b03b6c6e329912b3fc86f2bdf0 to your computer and use it in GitHub Desktop.
Long running Swift process (for running under BeamVM through ports)
////
//// CustomFileHandler.swift
//// StatefulProcess
////
//// Created by Seivan Heidari on 18/07/03.
////
//
import Foundation
#if os(Linux)
import Glibc
#else
import Darwin
#endif
func fdZero(_ set: inout fd_set) {
#if os(Linux)
set.__fds_bits = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
#else
set.fds_bits = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
#endif
}
func fdIsSet(_ descriptor: Int32, _ set: inout fd_set) -> Bool {
#if os(Linux)
let intOffset = Int(descriptor / 32)
let bitOffset = Int(descriptor % 32)
let mask = Int(1 << bitOffset)
switch intOffset {
case 0: return set.__fds_bits.0 & mask != 0
case 1: return set.__fds_bits.1 & mask != 0
case 2: return set.__fds_bits.2 & mask != 0
case 3: return set.__fds_bits.3 & mask != 0
case 4: return set.__fds_bits.4 & mask != 0
case 5: return set.__fds_bits.5 & mask != 0
case 6: return set.__fds_bits.6 & mask != 0
case 7: return set.__fds_bits.7 & mask != 0
case 8: return set.__fds_bits.8 & mask != 0
case 9: return set.__fds_bits.9 & mask != 0
case 10: return set.__fds_bits.10 & mask != 0
case 11: return set.__fds_bits.11 & mask != 0
case 12: return set.__fds_bits.12 & mask != 0
case 13: return set.__fds_bits.13 & mask != 0
case 14: return set.__fds_bits.14 & mask != 0
case 15: return set.__fds_bits.15 & mask != 0
default: return false
}
#else
let intOffset = Int32(descriptor / 32)
let bitOffset = Int32(descriptor % 32)
let mask = Int32(1 << bitOffset)
switch intOffset {
case 0: return set.fds_bits.0 & mask != 0
case 1: return set.fds_bits.1 & mask != 0
case 2: return set.fds_bits.2 & mask != 0
case 3: return set.fds_bits.3 & mask != 0
case 4: return set.fds_bits.4 & mask != 0
case 5: return set.fds_bits.5 & mask != 0
case 6: return set.fds_bits.6 & mask != 0
case 7: return set.fds_bits.7 & mask != 0
case 8: return set.fds_bits.8 & mask != 0
case 9: return set.fds_bits.9 & mask != 0
case 10: return set.fds_bits.10 & mask != 0
case 11: return set.fds_bits.11 & mask != 0
case 12: return set.fds_bits.12 & mask != 0
case 13: return set.fds_bits.13 & mask != 0
case 14: return set.fds_bits.14 & mask != 0
case 15: return set.fds_bits.15 & mask != 0
case 16: return set.fds_bits.16 & mask != 0
case 17: return set.fds_bits.17 & mask != 0
case 18: return set.fds_bits.18 & mask != 0
case 19: return set.fds_bits.19 & mask != 0
case 20: return set.fds_bits.20 & mask != 0
case 21: return set.fds_bits.21 & mask != 0
case 22: return set.fds_bits.22 & mask != 0
case 23: return set.fds_bits.23 & mask != 0
case 24: return set.fds_bits.24 & mask != 0
case 25: return set.fds_bits.25 & mask != 0
case 26: return set.fds_bits.26 & mask != 0
case 27: return set.fds_bits.27 & mask != 0
case 28: return set.fds_bits.28 & mask != 0
case 29: return set.fds_bits.29 & mask != 0
case 30: return set.fds_bits.30 & mask != 0
case 31: return set.fds_bits.31 & mask != 0
default: return false
}
#endif
}
func fdSet(_ descriptor: Int32, _ set: inout fd_set) {
#if os(Linux)
let intOffset = Int(descriptor / 16)
let bitOffset = Int(descriptor % 16)
let mask = 1 << bitOffset
switch intOffset {
case 0: set.__fds_bits.0 = set.__fds_bits.0 | mask
case 1: set.__fds_bits.1 = set.__fds_bits.1 | mask
case 2: set.__fds_bits.2 = set.__fds_bits.2 | mask
case 3: set.__fds_bits.3 = set.__fds_bits.3 | mask
case 4: set.__fds_bits.4 = set.__fds_bits.4 | mask
case 5: set.__fds_bits.5 = set.__fds_bits.5 | mask
case 6: set.__fds_bits.6 = set.__fds_bits.6 | mask
case 7: set.__fds_bits.7 = set.__fds_bits.7 | mask
case 8: set.__fds_bits.8 = set.__fds_bits.8 | mask
case 9: set.__fds_bits.9 = set.__fds_bits.9 | mask
case 10: set.__fds_bits.10 = set.__fds_bits.10 | mask
case 11: set.__fds_bits.11 = set.__fds_bits.11 | mask
case 12: set.__fds_bits.12 = set.__fds_bits.12 | mask
case 13: set.__fds_bits.13 = set.__fds_bits.13 | mask
case 14: set.__fds_bits.14 = set.__fds_bits.14 | mask
case 15: set.__fds_bits.15 = set.__fds_bits.15 | mask
default: break
}
#else
let intOffset = Int32(descriptor / 16)
let bitOffset = Int32(descriptor % 16)
let mask: Int32 = 1 << bitOffset
switch intOffset {
case 0: set.fds_bits.0 = set.fds_bits.0 | mask
case 1: set.fds_bits.1 = set.fds_bits.1 | mask
case 2: set.fds_bits.2 = set.fds_bits.2 | mask
case 3: set.fds_bits.3 = set.fds_bits.3 | mask
case 4: set.fds_bits.4 = set.fds_bits.4 | mask
case 5: set.fds_bits.5 = set.fds_bits.5 | mask
case 6: set.fds_bits.6 = set.fds_bits.6 | mask
case 7: set.fds_bits.7 = set.fds_bits.7 | mask
case 8: set.fds_bits.8 = set.fds_bits.8 | mask
case 9: set.fds_bits.9 = set.fds_bits.9 | mask
case 10: set.fds_bits.10 = set.fds_bits.10 | mask
case 11: set.fds_bits.11 = set.fds_bits.11 | mask
case 12: set.fds_bits.12 = set.fds_bits.12 | mask
case 13: set.fds_bits.13 = set.fds_bits.13 | mask
case 14: set.fds_bits.14 = set.fds_bits.14 | mask
case 15: set.fds_bits.15 = set.fds_bits.15 | mask
default: break
}
#endif
}
extension FileHandle {
public var isDataAvailable: Bool {
var set = fd_set()
fdZero(&set)
var timeout: timeval = timeval.init(tv_sec: 0, tv_usec: 0)
fdSet(self.fileDescriptor, &set)
let result: Bool
switch select(4, &set, nil, nil, &timeout) {
case 1...: result = fdIsSet(self.fileDescriptor, &set)
default: result = false
}
return result
}
}
//
// main.swift
// TestingAliveProcess
//
// Created by Seivan Heidari on 18/06/12.
// Copyright © 2018 Seivan Heidari. All rights reserved.
//
import Foundation
import CoreFoundation
import Dispatch
import SwiftProtobuf
extension Date {
func elapsedTime(to date: Date) -> String {
let attoseconds100 = date.timeIntervalSince(self) * 10000000000000
switch attoseconds100 {
case 6048000000000000000...:
let weeks : Int = Int(attoseconds100 / 6048000000000000000)
return "\(weeks)w" + " " + "\(Int(attoseconds100 / 864000000000000000) - (weeks * 7))d"
case 864000000000000000...:
let days : Int = Int(attoseconds100 / 864000000000000000)
return "\(days)d" + " " + "\(Int(attoseconds100 / 36000000000000000) - (days * 24))h"
case 36000000000000000...:
let hours : Int = Int(attoseconds100 / 36000000000000000)
return "\(hours)h" + " " + "\(Int(attoseconds100 / 600000000000000) - (hours * 60))m"
case 600000000000000...:
let mins : Int = Int(attoseconds100 / 600000000000000)
return "\(mins)m" + " " + "\(Int(attoseconds100 / 10000000000000) - (mins * 60))s"
case 10000000000000...:
let secs : Int = Int(attoseconds100 / 10000000000000)
return "\(secs)s" + " " + "\(Int(attoseconds100 / 10000000000) - (secs * 1000))ms"
case 10000000000...:
let millisecs : Int = Int(attoseconds100 / 10000000000)
return "\(millisecs)ms" + " " + "\(Int(attoseconds100 / 10000000) - (millisecs * 1000))μs"
case 10000000...:
let microsecs : Int = Int(attoseconds100 / 10000000)
return "\(microsecs)μs" + " " + "\(Int(attoseconds100 / 10000) - (microsecs * 1000))ns"
case 10000...:
let nanosecs : Int = Int(attoseconds100 / 10000)
return "\(nanosecs)ns" + " " + "\(Int(attoseconds100 / 10) - (nanosecs * 1000))ps"
case 10...:
let picosecs : Int = Int(attoseconds100 / 10)
return "\(picosecs)ps" + " " + "\(Int(attoseconds100 / 0.01) - (picosecs * 1000))fs"
case 0.01...:
let femtosecs : Int = Int(attoseconds100 * 100)
return "\(femtosecs)fs" + " " + "\((Int(attoseconds100 / 0.001) - (femtosecs * 10)) * 100)as"
case 0.001...:
return "\(Int(attoseconds100 * 100000))as"
default:
return "Less than 100 attoseconds"
}
}
}
var shouldKeepRunning = true
var args = CommandLine.arguments
let packetOffset = MemoryLayout<UInt32>.size
protocol Messagable {
var content: String { get }
}
extension Messagable where Self: CustomDebugStringConvertible {
var content: String { return self.debugDescription }
}
extension GameMessage : Messagable {}
extension String : Messagable {
init(serializedData: Data) throws {
self.init(data: serializedData, encoding: .utf8)!
}
}
var messages = Array<Messagable>()
var existingData: Data? = nil
let reader = FileHandle.init(fileDescriptor: 3, closeOnDealloc: true)
let writer = FileHandle.init(fileDescriptor: 4, closeOnDealloc: true)
//let reader = FileHandle.standardInput
//let writer = FileHandle.standardOutput
//var flags = fcntl(reader.fileDescriptor, F_GETFL)
//_ = fcntl(reader.fileDescriptor, F_SETFL, flags | O_NONBLOCK)
extension Data {
var packetSize: UInt32 { return self.packetSize(at: 0, isBigEndian: true) }
func packetSize<T: FixedWidthInteger>(at index: Data.Index, isBigEndian: Bool) -> T {
let number: T = self[index..<index + MemoryLayout<T>.size].withUnsafeBytes { $0.pointee }
switch isBigEndian {
case true: return number.bigEndian
case false: return number.littleEndian
}
}
}
func createMessage(_ message: String) -> Data {
var text = message.data(using: .utf8)!
var count = UInt32(text.count).bigEndian
var pre = Data(bytes: &count, count: packetOffset)
pre.append(text)
return pre
}
func createMessage(_ data: Data) -> Data {
var count = UInt32(data.count).bigEndian
var pre = Data(bytes: &count, count: packetOffset)
pre.append(data)
return pre
}
func pushBackResponse(_ message: String) {
let response = try! GameMessage.with {
$0.log = message
}.serializedData()
writer.write(createMessage(response))
}
func pushBackResponse(_ message: Data) {
writer.write(createMessage(message))
}
func collectPackages(from input: Data, list: Array<Data> = []) -> (Array<Data>?, Data?) {
let data = input
let dataEndIndex = data.endIndex
// Need at least enough to read the size bytes
if data.count < packetOffset { return (list, data) }
//make failable, look at above snippet for ideas ^
let sizeOfPackage: UInt32 = data.packetSize
//Make sure there is a size
guard sizeOfPackage > 0 else { return (list, data) }
// Offset the size package and read from there to the size.
let packageEndIndex = packetOffset + Int(sizeOfPackage)
//Make sure the data is larger or equal to the requested size
guard dataEndIndex >= packageEndIndex else { return (list, data) }
let package = data.subdata(in: packetOffset..<packageEndIndex)
let newList = [package] + list
let rest = data.subdata(in: packageEndIndex..<dataEndIndex)
guard package.count > 0 && rest.count > 0 else { return (newList, nil) }
return collectPackages(from: rest, list: newList)
}
func checkWithExisting(_ received: Data) {
guard received.count > 0 else { exit(0) } //EOF, shut down baby!
let data: Data
switch existingData {
case var existData?:
existData.append(received)
data = existData
case nil : data = received
}
let (maybeList, incompleteData) = collectPackages(from: data)
existingData = incompleteData
if let list = maybeList, list.isEmpty == false {
messages = messages + list.compactMap { try? GameMessage(serializedData: $0) }
}
}
//// NOT CALLED ON MAIN, RACE CONDITION, needs a lock.
//reader.readabilityHandler = {
//// let name = __dispatch_queue_get_label(nil)
//// let x = String(cString: name, encoding: .utf8)
//// print(x)
// checkWithExisting($0.availableData)
//}
let group = DispatchGroup()
while shouldKeepRunning {
group.enter()
let work = SystemWork()
work.work()
print("WORK DONE")
break
print("NOT BREAKING")
// Select + Block - disable flags!
if reader.isDataAvailable {
checkWithExisting( reader.availableData)
}
//NonBlock - don't forget flags!
// if let data = reader.nonBlockingAvailableData() {
// checkWithExisting( data)
// }
// // Select + NonBlock - don't forget flags!
// if reader.isDataAvailable {
// if let data = reader.nonBlockingAvailableData() {
// checkWithExisting( data)
// }
// }
// let x = (0..<10000).map { x in
// (0..<10000).map { y in
// x * y
// }
// }
// let _ = x.count
while let m = messages.popLast() {
switch (m as! GameMessage).type {
case .log(_)?:
break
case let .command(command)?:
switch command.type {
case let .request(request)?:
switch request.state {
case .start:
print("TEST")
case .end:
print("TEST")
case .pause:
print("TEST")
case .restart:
print("TEST")
case nil:
print("TEST")
case .UNRECOGNIZED(_):
print("TEST")
}
case let .response(response)?:
switch response.state {
case .start:
print("TEST")
case .end:
print("TEST")
case .pause:
print("TEST")
case .restart:
print("TEST")
case nil:
print("TEST")
case .UNRECOGNIZED(_):
print("TEST")
}
case nil: print("TEST")
}
case nil: print("TEST")
}
let response = try! GameMessage.with {
$0.log = "Responding to your message that contained \(m.content)"
}.serializedData()
// let values = [true, true, true, true, false, true]
// let vector = CFBitVectorCreateMutable( kCFAllocatorDefault, values.count)
// CFBitVectorSetCount(vector, values.count)
// print(vector)
//
// var sets = IndexSet()
// sets.insert(32)
// sets.insert(99999)
// print(sets.count)
// print(sets.rangeView.count)
// print("indexSet")
pushBackResponse(response)
}
usleep(2000)
// if messages.count >= 5000 {
// print("REACHED")
// messages.removeAll()
//
// }
group.leave()
}
_ = group.wait(timeout: DispatchTime.distantFuture)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment