/*
* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
* This source file is part of the Cangjie project, licensed under Apache-2.0
* with Runtime Library Exception.
*
* See https://cangjie-lang.cn/pages/LICENSE for license information.
*/
package std.runtime
import std.sync.*
import std.collection.*
@When[backend == "cjnative"]
@Intrinsic
func intrinsicRefEqFunc<A,R>(a: (A)->R, b: (A)->R): Bool
/**
* Check Whether two functions are the same
*/
@When[backend == "cjnative"]
func refEq<A,R>(a: (A)->R, b: (A)->R): Bool {
return intrinsicRefEqFunc(a, b)
}
@When[os != "Windows"]
public type SignalHandlerFunc = (Int32) -> Bool
@When[os != "Windows"]
private let mtx = Mutex()
@When[os != "Windows"]
class SignalManager {
static const MAX_SIG_NUM: Int32 = 32
static let sigSlot = Array<ArrayList<SignalHandlerFunc>>(Int64(MAX_SIG_NUM), { _ => ArrayList<SignalHandlerFunc>()})
// This memory allocation is globally unique and will only be released along with the program upon termination.
static let ptrAction = LibC.malloc<SignalAction>(count: 1)
static init() {
let action = SignalAction(signalHandler)
unsafe {
ptrAction.write(action)
}
}
}
/**
* Provides a way to register signal handlers in Cangjie.
* It uses a mutex to ensure thread safety when accessing the signal slots.
*/
@When[os != "Windows"]
public func registerSignalHandler(sig: Signal, handler: SignalHandlerFunc): Unit {
if (sig.value <= 0 || sig.value >= SignalManager.MAX_SIG_NUM) {
throw IllegalArgumentException("Signal value must > 0 and < ${SignalManager.MAX_SIG_NUM}.")
}
let slot = SignalManager.sigSlot[Int64(sig.value)]
synchronized(mtx) {
if (slot.size == 0) {
regSigChain(sig)
}
for (i in 0..slot.size) {
if (refEq<Int32, Bool>(slot[i], handler)) {
return
}
}
slot.add(handler)
}
}
/**
* Provides a way to unregister signal handlers in Cangjie.
* It uses a mutex to ensure thread safety when accessing the signal slots.
*/
@When[os != "Windows"]
public func unregisterSignalHandler(sig: Signal, handler: SignalHandlerFunc): Unit {
if (sig.value <= 0 || sig.value >= SignalManager.MAX_SIG_NUM) {
throw IllegalArgumentException("Signal value must > 0 and < ${SignalManager.MAX_SIG_NUM}.")
}
let slot = SignalManager.sigSlot[Int64(sig.value)]
synchronized(mtx) {
slot.removeIf({f => refEq<Int32, Bool>(f, handler)})
if (slot.size == 0) {
unregSigChain(sig)
}
}
}
/**
* Provides a way to reset signal handlers in Cangjie.
* It uses a mutex to ensure thread safety when accessing the signal slots.
*/
@When[os != "Windows"]
public func resetSignalHandler(sigs: Array<Signal>): Unit {
synchronized(mtx) {
if (sigs.size == 0) {
for (i in 0..SignalManager.sigSlot.size) {
if (!SignalManager.sigSlot[i].isEmpty()) {
SignalManager.sigSlot[i].clear()
unregSigChain(Signal(Int32(i), ""))
}
}
} else {
for (sig in sigs) {
if (sig.value <= 0 || sig.value >= SignalManager.MAX_SIG_NUM) {
throw IllegalArgumentException("Signal value must > 0 and < ${SignalManager.MAX_SIG_NUM}.")
}
SignalManager.sigSlot[Int64(sig.value)].clear()
unregSigChain(sig)
}
}
}
}
@When[os != "Windows"]
@C
func signalHandler(sig: Int32, _: CPointer<Unit>, _: CPointer<Unit>): Bool {
if (sig <= 0 || sig >= SignalManager.MAX_SIG_NUM) {
return false
}
let slot = SignalManager.sigSlot[Int64(sig)]
synchronized(mtx) {
for (fn in slot) {
let result = fn(sig)
if (result) {
return true
}
}
}
return false
}
@When[os != "Windows"]
func regSigChain(sig: Signal) {
unsafe {
CJ_MCC_AddSignalHandler(sig.value, SignalManager.ptrAction)
}
}
@When[os != "Windows"]
func unregSigChain(sig: Signal) {
unsafe {
CJ_MCC_RemoveSignalHandler(sig.value, signalHandler)
}
}
@When[os != "Windows"]
type SigHandlerCFunc = CFunc<(Int32, CPointer<Unit>, CPointer<Unit>) -> Bool>
@When[os != "Windows"]
@C
struct SignalAction {
let saHandler: SigHandlerCFunc
let scMask = 0u64
let scFlags = 0u64
init(fn: SigHandlerCFunc) {
saHandler = fn
}
}
@When[os != "Windows"]
foreign func CJ_MCC_AddSignalHandler(signal: Int32, sa: CPointer<SignalAction>): Unit
@When[os != "Windows"]
foreign func CJ_MCC_RemoveSignalHandler(signal: Int32, fn: SigHandlerCFunc): Unit
@When[os != "Windows"]
public class Signal {
public static const SIGHUP = Signal(0x1, "hangup")
public static const SIGINT = Signal(0x2, "interrupt")
public static const SIGQUIT = Signal(0x3, "quit")
public static const SIGTRAP = Signal(0x5, "trace/breakpoint trap")
public static const SIGALRM = Signal(0xe, "alarm clock")
public static const SIGTERM = Signal(0xf, "terminated")
let _value: Int32
let _comment: String
public const init(value: Int32, comment: String) {
this._value = value
this._comment = comment
}
public prop value: Int32 {
get() {
this._value
}
}
}