Merge pull request #13 from petersktang/main
Fix Concurrency Thread-safe exception
This commit is contained in:
@@ -76,7 +76,9 @@ private class BundleManager {
|
|||||||
private var ctFonts = [CTFontSizePair: CTFont]()
|
private var ctFonts = [CTFontSizePair: CTFont]()
|
||||||
private var rawMathTables = [MathFont: NSDictionary]()
|
private var rawMathTables = [MathFont: NSDictionary]()
|
||||||
|
|
||||||
private let threadSafeQueue = DispatchQueue(label: "com.smartmath.mathfont.threadsafequeue", attributes: .concurrent)
|
private let threadSafeQueue = DispatchQueue(label: "com.smartmath.mathfont.threadsafequeue",
|
||||||
|
qos: .userInitiated,
|
||||||
|
attributes: .concurrent)
|
||||||
|
|
||||||
private func registerCGFont(mathFont: MathFont) throws {
|
private func registerCGFont(mathFont: MathFont) throws {
|
||||||
guard let frameworkBundleURL = Bundle.module.url(forResource: "mathFonts", withExtension: "bundle"),
|
guard let frameworkBundleURL = Bundle.module.url(forResource: "mathFonts", withExtension: "bundle"),
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ extension MathImage {
|
|||||||
intrinsicContentSize = intrinsicContentSize(displayList)
|
intrinsicContentSize = intrinsicContentSize(displayList)
|
||||||
displayList.textColor = textColor
|
displayList.textColor = textColor
|
||||||
|
|
||||||
let size = intrinsicContentSize
|
let size = intrinsicContentSize.regularized
|
||||||
layoutImage(size: size, displayList: displayList)
|
layoutImage(size: size, displayList: displayList)
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@@ -107,3 +107,8 @@ private extension CGAffineTransform {
|
|||||||
return transform
|
return transform
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
extension CGSize {
|
||||||
|
fileprivate var regularized: CGSize {
|
||||||
|
CGSize(width: ceil(width), height: ceil(height))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ public class MTMathAtomFactory {
|
|||||||
"rfloor" : "\u{230B}"
|
"rfloor" : "\u{230B}"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
private static let delimValueLock = NSLock()
|
||||||
static var _delimValueToName = [String: String]()
|
static var _delimValueToName = [String: String]()
|
||||||
public static var delimValueToName: [String: String] {
|
public static var delimValueToName: [String: String] {
|
||||||
if _delimValueToName.isEmpty {
|
if _delimValueToName.isEmpty {
|
||||||
@@ -78,7 +79,11 @@ public class MTMathAtomFactory {
|
|||||||
}
|
}
|
||||||
output[value] = key
|
output[value] = key
|
||||||
}
|
}
|
||||||
_delimValueToName = output
|
delimValueLock.lock()
|
||||||
|
defer { delimValueLock.unlock() }
|
||||||
|
if _delimValueToName.isEmpty {
|
||||||
|
_delimValueToName = output
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return _delimValueToName
|
return _delimValueToName
|
||||||
}
|
}
|
||||||
@@ -98,6 +103,7 @@ public class MTMathAtomFactory {
|
|||||||
"widetilde" : "\u{0303}"
|
"widetilde" : "\u{0303}"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
private static let accentValueLock = NSLock()
|
||||||
static var _accentValueToName: [String: String]? = nil
|
static var _accentValueToName: [String: String]? = nil
|
||||||
public static var accentValueToName: [String: String] {
|
public static var accentValueToName: [String: String] {
|
||||||
if _accentValueToName == nil {
|
if _accentValueToName == nil {
|
||||||
@@ -115,7 +121,11 @@ public class MTMathAtomFactory {
|
|||||||
}
|
}
|
||||||
output[value] = key
|
output[value] = key
|
||||||
}
|
}
|
||||||
_accentValueToName = output
|
accentValueLock.lock()
|
||||||
|
defer { accentValueLock.unlock() }
|
||||||
|
if _accentValueToName == nil {
|
||||||
|
_accentValueToName = output
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return _accentValueToName!
|
return _accentValueToName!
|
||||||
}
|
}
|
||||||
@@ -390,6 +400,7 @@ public class MTMathAtomFactory {
|
|||||||
"scriptscriptstyle" : MTMathStyle(style: .scriptOfScript),
|
"scriptscriptstyle" : MTMathStyle(style: .scriptOfScript),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
private static let textToLatexLock = NSLock()
|
||||||
static var _textToLatexSymbolName: [String: String]? = nil
|
static var _textToLatexSymbolName: [String: String]? = nil
|
||||||
public static var textToLatexSymbolName: [String: String] {
|
public static var textToLatexSymbolName: [String: String] {
|
||||||
get {
|
get {
|
||||||
@@ -413,13 +424,17 @@ public class MTMathAtomFactory {
|
|||||||
}
|
}
|
||||||
output[atom.nucleus] = key
|
output[atom.nucleus] = key
|
||||||
}
|
}
|
||||||
self._textToLatexSymbolName = output
|
textToLatexLock.lock()
|
||||||
|
defer { textToLatexLock.unlock() }
|
||||||
|
if self._textToLatexSymbolName == nil {
|
||||||
|
self._textToLatexSymbolName = output
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return self._textToLatexSymbolName!
|
return self._textToLatexSymbolName!
|
||||||
}
|
}
|
||||||
set {
|
// set {
|
||||||
self._textToLatexSymbolName = newValue
|
// self._textToLatexSymbolName = newValue
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// public static let sharedInstance = MTMathAtomFactory()
|
// public static let sharedInstance = MTMathAtomFactory()
|
||||||
@@ -603,8 +618,13 @@ public class MTMathAtomFactory {
|
|||||||
e.g. to define a symbol for "lcm" one can call:
|
e.g. to define a symbol for "lcm" one can call:
|
||||||
`MTMathAtomFactory.add(latexSymbol:"lcm", value:MTMathAtomFactory.operatorWithName("lcm", limits: false))` */
|
`MTMathAtomFactory.add(latexSymbol:"lcm", value:MTMathAtomFactory.operatorWithName("lcm", limits: false))` */
|
||||||
public static func add(latexSymbol name: String, value: MTMathAtom) {
|
public static func add(latexSymbol name: String, value: MTMathAtom) {
|
||||||
|
let _ = Self.textToLatexSymbolName
|
||||||
|
// above force textToLatexSymbolName to instantiate first, _textToLatexSymbolName also initialized.
|
||||||
|
textToLatexLock.lock()
|
||||||
|
defer { textToLatexLock.unlock() }
|
||||||
supportedLatexSymbols[name] = value
|
supportedLatexSymbols[name] = value
|
||||||
Self.textToLatexSymbolName[value.nucleus] = name
|
// below update the underlying dictionary entry.
|
||||||
|
Self._textToLatexSymbolName?[value.nucleus] = name
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a large opertor for the given name. If limits is true, limits are set up on
|
/** Returns a large opertor for the given name. If limits is true, limits are set up on
|
||||||
|
|||||||
@@ -21,9 +21,15 @@ enum InterElementSpaceType : Int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var interElementSpaceArray = [[InterElementSpaceType]]()
|
var interElementSpaceArray = [[InterElementSpaceType]]()
|
||||||
|
private let interElementLock = NSLock()
|
||||||
|
|
||||||
func getInterElementSpaces() -> [[InterElementSpaceType]] {
|
func getInterElementSpaces() -> [[InterElementSpaceType]] {
|
||||||
if interElementSpaceArray.isEmpty {
|
if interElementSpaceArray.isEmpty {
|
||||||
|
|
||||||
|
interElementLock.lock()
|
||||||
|
defer { interElementLock.unlock() }
|
||||||
|
guard interElementSpaceArray.isEmpty else { return interElementSpaceArray }
|
||||||
|
|
||||||
interElementSpaceArray =
|
interElementSpaceArray =
|
||||||
// ordinary operator binary relation open close punct fraction
|
// ordinary operator binary relation open close punct fraction
|
||||||
[ [.none, .thin, .nsMedium, .nsThick, .none, .none, .none, .nsThin], // ordinary
|
[ [.none, .thin, .nsMedium, .nsThick, .none, .none, .none, .nsThin], // ordinary
|
||||||
|
|||||||
47
Tests/SwiftMathTests/ConcurrencyThreadsafeTests.swift
Normal file
47
Tests/SwiftMathTests/ConcurrencyThreadsafeTests.swift
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// ConcurrencyThreadsafeTests.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Peter Tang on 26/9/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import SwiftMath
|
||||||
|
|
||||||
|
final class ConcurrencyThreadsafeTests: XCTestCase {
|
||||||
|
|
||||||
|
private let executionQueue = DispatchQueue(label: "com.swiftmath.concurrencytests", attributes: .concurrent)
|
||||||
|
private let executionGroup = DispatchGroup()
|
||||||
|
|
||||||
|
let totalCases = 20
|
||||||
|
var testCount = 0
|
||||||
|
|
||||||
|
func testSwiftMathConcurrentScript() throws {
|
||||||
|
for caseNumber in 0 ..< totalCases {
|
||||||
|
helperConcurrency(caseNumber, in: executionGroup, on: executionQueue) {
|
||||||
|
let result1 = getInterElementSpaces()
|
||||||
|
let result2 = MTMathAtomFactory.delimValueToName
|
||||||
|
let result3 = MTMathAtomFactory.accentValueToName
|
||||||
|
let result4 = MTMathAtomFactory.textToLatexSymbolName
|
||||||
|
XCTAssertNotNil(result1)
|
||||||
|
XCTAssertNotNil(result2)
|
||||||
|
XCTAssertNotNil(result3)
|
||||||
|
XCTAssertNotNil(result4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
executionGroup.notify(queue: .main) { [weak self] in
|
||||||
|
// print("All test cases completed: \(self?.testCount ?? 0)")
|
||||||
|
}
|
||||||
|
executionGroup.wait()
|
||||||
|
}
|
||||||
|
func helperConcurrency(_ count: Int, in group: DispatchGroup, on queue: DispatchQueue, _ testClosure: @escaping () -> (Void)) {
|
||||||
|
let workitem = DispatchWorkItem {
|
||||||
|
testClosure()
|
||||||
|
}
|
||||||
|
workitem.notify(queue: .main) { [weak self] in
|
||||||
|
self?.testCount += 1
|
||||||
|
}
|
||||||
|
queue.async(group: group, execute: workitem)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user