Skip to content

Commit

Permalink
ensure autoloading kernel32.dll threading API
Browse files Browse the repository at this point in the history
  • Loading branch information
mratsim committed Dec 3, 2023
1 parent 143fd8c commit 9d5e621
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ build/
*.so.*
*.dylib
*.a
*.lib
*.la
*.dll
*.exe
Expand Down
48 changes: 48 additions & 0 deletions bindings/lib_autoload.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Constantine
# Copyright (c) 2018-2019 Status Research & Development GmbH
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.

import ../constantine/platforms/loadtime_functions

# When Constantine is built as a library, we want to minimize friction of using it.
# Hence we want to users to be able to directly use it without special ceremony.
#
# This is possible for dynamic libraries if --noMain isn't used.
#
# https://github.com/nim-lang/Nim/blob/v2.0.0/compiler/cgen.nim#L1572-L1583
# https://github.com/nim-lang/Nim/blob/v2.0.0/lib/nimbase.h#L513
#
# The function DllMain is autoloaded on Windows
# Functions tagged __attribute__((constructor)) are autoloaded on UNIX OSes
#
# Alas, Nim doesn't provide such facilities for static libraries
# so we provide our own {.loadTime.} macro for autoloading:
# - any proc
# - on any OS
# - whether using a dynamic or static library
#
# We use them for runtime CPU features detection.
#
# And on Windows as functions in DLLs (kernel APIs for the threadpool for example) are loaded
# as global variables

when defined(windows) and (appType == "lib" or appType == "staticlib"):
proc ctt_init_NimMain() {.importc, cdecl.}
## Setup Nim globals, including loading library dependencies on Windows
## We assume that Constantine was compiled with --nimMainPrefix:ctt_init_

proc ctt_autoload_NimMain() {.load_time.} =
## Autosetup Constantine globals on library load.
## This must be referenced from an another module
## to not be optimized away by the static linker
ctt_init_NimMain()

proc ctt_autoloader_addr*(): pointer =
## This returns an runtime reference to the autoloader
## so that it cannot be optimized away.
## Compare it with "nil"
cast[pointer](ctt_autoload_NimMain)
5 changes: 4 additions & 1 deletion bindings/lib_constantine.nim
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ import
./lib_hashes,
./lib_curves,
# Protocols
../constantine/ethereum_bls_signatures
../constantine/ethereum_bls_signatures,

# Ensure globals like proc from kernel32.dll are populated at library load time
./lib_autoload
10 changes: 9 additions & 1 deletion constantine-rust/constantine-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,15 @@ fn main() {
.expect("failed to execute process");

println!("cargo:rustc-link-search=native={}", out_dir.display());
println!("cargo:rustc-link-lib=static=constantine");

// On windows stable channel (msvc) expects constantine.lib
// while stable-gnu channel (gcc) expects libconstantine.a
// hence we use +verbatim
#[cfg(target_family = "windows")]
println!("cargo:rustc-link-lib=static:+verbatim=constantine.lib");

#[cfg(target_family = "unix")]
println!("cargo:rustc-link-lib=static:+verbatim=libconstantine.a");

// Avoid full recompilation
println!("cargo:rerun-if-changed=build.rs");
Expand Down
10 changes: 10 additions & 0 deletions constantine.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,18 @@ proc releaseBuildOptions(buildMode = bmBinary): string =
elif forceLto: ltoFlags
else: ""

let osSpecific =
# Remove the auto __chkstk, which are: 1. slower, 2. not supported on Rust "stable-gnu" channel.
if defined(windows): " --passC:-mno-stack-arg-probe "
else: ""

let threadLocalStorage = " --tlsEmulation=off "

compiler &
envASM & env32 &
ltoOptions &
osSpecific &
threadLocalStorage &
compilerFlags()

proc genDynamicLib(outdir, nimcache: string) =
Expand Down Expand Up @@ -292,6 +301,7 @@ task test_lib, "Test C library":
exec "mkdir -p build/test_lib"
testLib("examples_c", "t_libctt_bls12_381", useGMP = true)
testLib("examples_c", "ethereum_bls_signatures", useGMP = false)
testLib("tests"/"c_api", "t_threadpool", useGMP = false)

# Test config
# ----------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion constantine/platforms/loadtime_functions.nim
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ macro loadTime*(procAst: untyped): untyped =
result = procAst

elif defined(vcc):
warning "CPU feature autodetection at Constantine load time has not been tested with MSVC"
warning "Constantine autoloaded functions have not been tested with MSVC"

template msvcInitSection(procDef: untyped): untyped =
let procName = astToStr(def)
Expand Down
24 changes: 24 additions & 0 deletions constantine/threadpool/dll_autoload.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Constantine
# Copyright (c) 2018-2019 Status Research & Development GmbH
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.

# To ensure that __attribute__((constructor)) procs are not
# removed because they are unaccessible, we force symbol reference
when defined(windows) and (appType == "lib" or appType == "staticlib"):
import ../../bindings/lib_autoload

proc check_lib_dependency_loader*() =
## This prevents the linker from deleting our constructor function
## that loads Windows kernel, synchronization and threading related functions.
## We only need to have any symbol in the translation unit being used.
doAssert ctt_autoloader_addr() != nil
else:
template check_lib_dependency_loader*() =
## This prevents the linker from deleting our constructor function
## that loads Windows kernel, synchronization and threading related functions.
## We only need to have any symbol in the translation unit being used.
discard
8 changes: 5 additions & 3 deletions constantine/threadpool/threadpool.nim
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import
./parallel_offloading,
../platforms/[allocs, bithacks]

import ../zoo_exports
import ../zoo_exports, dll_autoload

export
# flowvars
Expand Down Expand Up @@ -953,18 +953,20 @@ proc new*(T: type Threadpool, numThreads = countProcessors()): T {.raises: [Reso

type TpObj = typeof(default(Threadpool)[]) # due to C import, we need a dynamic sizeof
let tp = allocHeapUncheckedAlignedPtr(Threadpool, sizeof(TpObj), alignment = 64)

tp.barrier.init(numThreads.uint32)
tp.globalBackoff.initialize()
tp.numThreads = numThreads.int32
tp.workerQueues = allocHeapArrayAligned(Taskqueue, numThreads, alignment = 64)
tp.workers = allocHeapArrayAligned(Thread[(Threadpool, WorkerID)], numThreads, alignment = 64)
tp.workerSignals = allocHeapArrayAligned(Signal, numThreads, alignment = 64)

# Setup master thread
workerContext.id = 0
workerContext.threadpool = tp

# We use kernel functions for threading and synchronization next,
# ensure they are loaded and static constructor procs are not removed by the linker.
check_lib_dependency_loader()

# Start worker threads
for i in 1 ..< numThreads:
createThread(tp.workers[i], workerEntryFn, (tp, WorkerID(i)))
Expand Down
5 changes: 5 additions & 0 deletions include/constantine.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
#ifndef __CTT_H_CONSTANTINE__
#define __CTT_H_CONSTANTINE__

// Core functions
#include "constantine/core/datatypes.h"
#include "constantine/core/serialization.h"
#include "constantine/core/threadpool.h"

// Hash functions
#include "constantine/hashes/sha256.h"

Expand Down
30 changes: 30 additions & 0 deletions tests/c_api/t_threadpool.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/** Constantine
* Copyright (c) 2018-2019 Status Research & Development GmbH
* Copyright (c) 2020-Present Mamy André-Ratsimbazafy
* Licensed and distributed under either of
* * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
* * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
* at your option. This file may not be copied, modified, or distributed except according to those terms.
*/

// This is a test to ensure the C API for the threadpool works on all platforms
// and:
// if special options like -d:tlsEmulation:off are needed
// if NimMain is done implicitly at load time.

#include <stdio.h>
#include <constantine.h>

void ctt_init_NimMain(void);

int main(){
printf("Constantine: Testing the C API for the threadpool.\n");
// ctt_init_NimMain();

struct ctt_threadpool* tp = ctt_threadpool_new(4);
printf("Constantine: Threadpool init successful.\n");
ctt_threadpool_shutdown(tp);
printf("Constantine: Threadpool shutdown successful.\n");

return 0;
}

0 comments on commit 9d5e621

Please sign in to comment.