Testing fatalError and friends

Don't use this code
You can use signal handlers to catch failures.

// C header:
typedef void(func_with_ctx_t)(void *ctx);
bool with_signal_handler(int sig, func_with_ctx_t *func, void *ctx);

// C source:
typedef void(c_signal_handler_t)(int);

typedef struct {
  int *jb;
} resume_vector_t;

#define SIG_COUNT (SIGUSR2+1)

resume_vector_t resume_vectors[SIG_COUNT];

void signal_handler(int sig) {
  resume_vector_t rv = resume_vectors[sig];
  longjmp(rv.jb, 1);
}

bool with_signal_handler(int sig, func_with_ctx_t *func, void *ctx) {
  c_signal_handler_t *old_handler = signal(sig, signal_handler);
  resume_vector_t old_rv = resume_vectors[sig];

  jmp_buf jb;
  bool success = false;
  if (setjmp(jb) == 0) {
    resume_vectors[sig].jb = jb;
    func(ctx);
    success = true;
  }

  // restore
  resume_vectors[sig] = old_rv;
  signal(sig, old_handler);

  return success;
}
// Swift:

struct SignalReceived: Error {}
struct InternalInconsistency: Error {}

func withSignalHandler<R>(signal: Int32, do body: () throws -> R) throws -> R {
  try withoutActuallyEscaping(body) { body in
    typealias BodyWrapped = () -> Void

    var result: Result<R, Error>?
    let bodyWrapped: BodyWrapped = {
      result = Result(catching: body)
    }
    let ctx = Unmanaged.passRetained(bodyWrapped as AnyObject)

    let success = with_signal_handler(signal, { ptr in
      guard let ptr else {
        return
      }
      let bodyWrappedBoxed = Unmanaged<AnyObject>.fromOpaque(ptr).takeRetainedValue()
      let bodyWrapped = bodyWrappedBoxed as! BodyWrapped
      bodyWrapped()
    }, ctx.toOpaque())

    if success {
      guard let result else {
        assertionFailure()
        throw InternalInconsistency()
      }
      return try result.get()
    }
    throw SignalReceived()
  }
}


@main
struct App {
  static func main() {
    do {
      let r: Int = try withSignalHandler(signal: SIGTRAP) {
        _ = [][0]
        print("Unreached 1")
        return 42
      }
      print("Unreached 2", r)
    } catch {
      print(error)
    }
    print("Resume")
  }
}
// Will print:
// Swift/ContiguousArrayBuffer.swift:600: Fatal error: Index out of range
// SignalReceived()
// Resume

2 Likes