From d13929706a3b45bd64e0a87e0afc3d45625e888d Mon Sep 17 00:00:00 2001 From: Alexey Melnichuk Date: Tue, 11 Feb 2014 17:32:59 +0400 Subject: Init LuaDist for llthreads2 module. --- .travis.yml | 67 ++ CMakeLists.txt | 17 + cmake/FindLua.cmake | 127 ++++ cmake/dist.cmake | 321 +++++++++ cmake/lua.cmake | 309 +++++++++ dist.info | 12 + llthreads2/.gitignore | 18 + llthreads2/.travis.yml | 42 ++ llthreads2/.travis/setup_lua.sh | 35 + llthreads2/CMakeLists.txt | 17 + llthreads2/COPYRIGHT.llthreads | 19 + llthreads2/LICENSE | 20 + llthreads2/README.md | 113 ++++ llthreads2/cmake/FindLua.cmake | 127 ++++ llthreads2/cmake/dist.cmake | 321 +++++++++ llthreads2/cmake/lua.cmake | 309 +++++++++ llthreads2/dist.info | 12 + llthreads2/lakeconfig.lua | 252 +++++++ llthreads2/lakefile | 45 ++ llthreads2/msvc/llthreads.sln | 20 + llthreads2/msvc/llthreads.vcproj | 208 ++++++ .../rockspecs/lua-llthreads2-0.1.0-1.rockspec | 44 ++ .../rockspecs/lua-llthreads2-0.1.1-1.rockspec | 44 ++ .../lua-llthreads2-compat-0.1.0-1.rockspec | 44 ++ .../lua-llthreads2-compat-0.1.1-1.rockspec | 44 ++ .../rockspecs/lua-llthreads2-compat-scm-0.rockspec | 44 ++ llthreads2/rockspecs/lua-llthreads2-scm-0.rockspec | 44 ++ llthreads2/src/copy.inc | 170 +++++ llthreads2/src/l52util.c | 126 ++++ llthreads2/src/l52util.h | 57 ++ llthreads2/src/llthread.c | 746 +++++++++++++++++++++ llthreads2/src/traceback.inc | 56 ++ llthreads2/test/test_alive.lua | 35 + llthreads2/test/test_join_detach.lua | 59 ++ llthreads2/test/test_join_error.lua | 24 + llthreads2/test/test_join_timeout.lua | 23 + llthreads2/test/test_llthreads.lua | 60 ++ llthreads2/test/test_load_llthreads2.lua | 7 + llthreads2/test/test_logger.lua | 24 + llthreads2/test/test_pass_cfunction.lua | 17 + llthreads2/test/test_register_ffi.lua | 14 + llthreads2/test/test_register_llthreads.lua | 15 + llthreads2/test/test_table_copy.lua | 134 ++++ llthreads2/test/test_threads.lua | 67 ++ llthreads2/test/utils.lua | 63 ++ 45 files changed, 4372 insertions(+) create mode 100644 .travis.yml create mode 100644 CMakeLists.txt create mode 100644 cmake/FindLua.cmake create mode 100644 cmake/dist.cmake create mode 100644 cmake/lua.cmake create mode 100644 dist.info create mode 100644 llthreads2/.gitignore create mode 100644 llthreads2/.travis.yml create mode 100644 llthreads2/.travis/setup_lua.sh create mode 100644 llthreads2/CMakeLists.txt create mode 100644 llthreads2/COPYRIGHT.llthreads create mode 100644 llthreads2/LICENSE create mode 100644 llthreads2/README.md create mode 100644 llthreads2/cmake/FindLua.cmake create mode 100644 llthreads2/cmake/dist.cmake create mode 100644 llthreads2/cmake/lua.cmake create mode 100644 llthreads2/dist.info create mode 100644 llthreads2/lakeconfig.lua create mode 100644 llthreads2/lakefile create mode 100644 llthreads2/msvc/llthreads.sln create mode 100644 llthreads2/msvc/llthreads.vcproj create mode 100644 llthreads2/rockspecs/lua-llthreads2-0.1.0-1.rockspec create mode 100644 llthreads2/rockspecs/lua-llthreads2-0.1.1-1.rockspec create mode 100644 llthreads2/rockspecs/lua-llthreads2-compat-0.1.0-1.rockspec create mode 100644 llthreads2/rockspecs/lua-llthreads2-compat-0.1.1-1.rockspec create mode 100644 llthreads2/rockspecs/lua-llthreads2-compat-scm-0.rockspec create mode 100644 llthreads2/rockspecs/lua-llthreads2-scm-0.rockspec create mode 100644 llthreads2/src/copy.inc create mode 100644 llthreads2/src/l52util.c create mode 100644 llthreads2/src/l52util.h create mode 100644 llthreads2/src/llthread.c create mode 100644 llthreads2/src/traceback.inc create mode 100644 llthreads2/test/test_alive.lua create mode 100644 llthreads2/test/test_join_detach.lua create mode 100644 llthreads2/test/test_join_error.lua create mode 100644 llthreads2/test/test_join_timeout.lua create mode 100644 llthreads2/test/test_llthreads.lua create mode 100644 llthreads2/test/test_load_llthreads2.lua create mode 100644 llthreads2/test/test_logger.lua create mode 100644 llthreads2/test/test_pass_cfunction.lua create mode 100644 llthreads2/test/test_register_ffi.lua create mode 100644 llthreads2/test/test_register_llthreads.lua create mode 100644 llthreads2/test/test_table_copy.lua create mode 100644 llthreads2/test/test_threads.lua create mode 100644 llthreads2/test/utils.lua diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..3bb3efa --- /dev/null +++ b/.travis.yml @@ -0,0 +1,67 @@ +# +# LuaDist Travis-CI Hook +# + +# We assume C build environments +language: C + +# Try using multiple Lua Implementations +env: + - TOOL="gcc" # Use native compiler (GCC usually) + - TOOL="clang" # Use clang + - TOOL="i686-w64-mingw32" # 32bit MinGW + - TOOL="x86_64-w64-mingw32" # 64bit MinGW + - TOOL="arm-linux-gnueabihf" # ARM hard-float (hf), linux + +# Crosscompile builds may fail +matrix: + allow_failures: + - env: TOOL="i686-w64-mingw32" + - env: TOOL="x86_64-w64-mingw32" + - env: TOOL="arm-linux-gnueabihf" + +# Install dependencies +install: + - git clone git://github.com/LuaDist/Tools.git ~/_tools + - ~/_tools/travis/travis install + +# Bootstap +before_script: + - ~/_tools/travis/travis bootstrap + +# Build the module +script: + # - ~/_tools/travis/travis build + - ~/luadist _test install lua-5.1.5 -binary=false -verbose -DCMAKE_TOOLCHAIN_FILE=$TOOLFILE + - ~/luadist _test make . -binary=false -verbose -DCMAKE_TOOLCHAIN_FILE=$TOOLFILE + - cd _test/share/lua-llthreads2/test + - $TRAVIS_BUILD_DIR/_test/bin/lua test_table_copy.lua + - $TRAVIS_BUILD_DIR/_test/bin/lua test_threads.lua + - $TRAVIS_BUILD_DIR/_test/bin/lua test_llthreads.lua + - $TRAVIS_BUILD_DIR/_test/bin/lua test_join_timeout.lua + - $TRAVIS_BUILD_DIR/_test/bin/lua test_join_detach.lua + - $TRAVIS_BUILD_DIR/_test/bin/lua test_join_error.lua + - $TRAVIS_BUILD_DIR/_test/bin/lua test_register_ffi.lua + - $TRAVIS_BUILD_DIR/_test/bin/lua test_logger.lua + - $TRAVIS_BUILD_DIR/_test/bin/lua test_pass_cfunction.lua + - $TRAVIS_BUILD_DIR/_test/bin/lua test_load_llthreads2.lua + - $TRAVIS_BUILD_DIR/_test/bin/lua test_alive.lua + + +# Execute additional tests or commands +after_script: + - cd $TRAVIS_BUILD_DIR + - ~/_tools/travis/travis test + +# Only watch the master branch +branches: + only: + - dist-llthreads2 + +# Notify the LuaDist Dev group if needed +notifications: + recipients: + - luadist-dev@googlegroups.com + email: + on_success: change + on_failure: always diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..596e496 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required ( VERSION 2.8 ) + +project ( lua-llthreads2 C ) +include ( cmake/dist.cmake ) +include ( lua ) + +set ( CMAKE_THREAD_PREFER_PTHREAD TRUE ) +include ( FindThreads ) + +include_directories ( ${CMAKE_CURRENT_SOURCE_DIR}/llthreads2/src ) + +set ( LUA_LLTHREADS_SRC llthreads2/src/l52util.c llthreads2/src/llthread.c ) + +install_lua_module ( llthreads2 ${LUA_LLTHREADS_SRC} LINK ${CMAKE_THREAD_LIBS_INIT} ) + +install_data ( llthreads2/COPYRIGHT.llthreads llthreads2/README.md llthreads2/LICENSE ) +install_test ( llthreads2/test/ ) diff --git a/cmake/FindLua.cmake b/cmake/FindLua.cmake new file mode 100644 index 0000000..6991b4a --- /dev/null +++ b/cmake/FindLua.cmake @@ -0,0 +1,127 @@ +# Locate Lua library +# This module defines +# LUA_EXECUTABLE, if found +# LUA_FOUND, if false, do not try to link to Lua +# LUA_LIBRARIES +# LUA_INCLUDE_DIR, where to find lua.h +# LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8) +# +# Note that the expected include convention is +# #include "lua.h" +# and not +# #include +# This is because, the lua location is not standardized and may exist +# in locations other than lua/ + +#============================================================================= +# Copyright 2007-2009 Kitware, Inc. +# Modified to support Lua 5.2 by LuaDist 2012 +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) +# +# The required version of Lua can be specified using the +# standard syntax, e.g. FIND_PACKAGE(Lua 5.1) +# Otherwise the module will search for any available Lua implementation + +# Always search for non-versioned lua first (recommended) +SET(_POSSIBLE_LUA_INCLUDE include include/lua) +SET(_POSSIBLE_LUA_EXECUTABLE lua) +SET(_POSSIBLE_LUA_LIBRARY lua) + +# Determine possible naming suffixes (there is no standard for this) +IF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) + SET(_POSSIBLE_SUFFIXES "${Lua_FIND_VERSION_MAJOR}${Lua_FIND_VERSION_MINOR}" "${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}" "-${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}") +ELSE(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) + SET(_POSSIBLE_SUFFIXES "52" "5.2" "-5.2" "51" "5.1" "-5.1") +ENDIF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) + +# Set up possible search names and locations +FOREACH(_SUFFIX ${_POSSIBLE_SUFFIXES}) + LIST(APPEND _POSSIBLE_LUA_INCLUDE "include/lua${_SUFFIX}") + LIST(APPEND _POSSIBLE_LUA_EXECUTABLE "lua${_SUFFIX}") + LIST(APPEND _POSSIBLE_LUA_LIBRARY "lua${_SUFFIX}") +ENDFOREACH(_SUFFIX) + +# Find the lua executable +FIND_PROGRAM(LUA_EXECUTABLE + NAMES ${_POSSIBLE_LUA_EXECUTABLE} +) + +# Find the lua header +FIND_PATH(LUA_INCLUDE_DIR lua.h + HINTS + $ENV{LUA_DIR} + PATH_SUFFIXES ${_POSSIBLE_LUA_INCLUDE} + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) + +# Find the lua library +FIND_LIBRARY(LUA_LIBRARY + NAMES ${_POSSIBLE_LUA_LIBRARY} + HINTS + $ENV{LUA_DIR} + PATH_SUFFIXES lib64 lib + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw + /opt/local + /opt/csw + /opt +) + +IF(LUA_LIBRARY) + # include the math library for Unix + IF(UNIX AND NOT APPLE) + FIND_LIBRARY(LUA_MATH_LIBRARY m) + SET( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") + # For Windows and Mac, don't need to explicitly include the math library + ELSE(UNIX AND NOT APPLE) + SET( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") + ENDIF(UNIX AND NOT APPLE) +ENDIF(LUA_LIBRARY) + +# Determine Lua version +IF(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") + FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua .+\"") + + STRING(REGEX REPLACE "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([^\"]+)\".*" "\\1" LUA_VERSION_STRING "${lua_version_str}") + UNSET(lua_version_str) +ENDIF() + +# Lua 5.2 +IF(NOT LUA_VERSION_STRING) + FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define LUA_VERSION_[A-Z]+[ \t]+\"[0-9]+\"") + STRING(REGEX REPLACE ".*#define LUA_VERSION_MAJOR[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_MAJOR ${lua_version_str}) + STRING(REGEX REPLACE ".*#define LUA_VERSION_MINOR[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_MINOR ${lua_version_str}) + STRING(REGEX REPLACE ".*#define LUA_VERSION_RELEASE[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_RELEASE ${lua_version_str}) + SET(LUA_VERSION_STRING ${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_RELEASE}) +ENDIF() + +INCLUDE(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if +# all listed variables are TRUE +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua + REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR + VERSION_VAR LUA_VERSION_STRING) + +MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY LUA_EXECUTABLE) + diff --git a/cmake/dist.cmake b/cmake/dist.cmake new file mode 100644 index 0000000..310ef94 --- /dev/null +++ b/cmake/dist.cmake @@ -0,0 +1,321 @@ +# LuaDist CMake utility library. +# Provides sane project defaults and macros common to LuaDist CMake builds. +# +# Copyright (C) 2007-2012 LuaDist. +# by David Manura, Peter Drahoš +# Redistribution and use of this file is allowed according to the terms of the MIT license. +# For details see the COPYRIGHT file distributed with LuaDist. +# Please note that the package source code is licensed under its own license. + +## Extract information from dist.info +if ( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/dist.info ) + message ( FATAL_ERROR + "Missing dist.info file (${CMAKE_CURRENT_SOURCE_DIR}/dist.info)." ) +endif () +file ( READ ${CMAKE_CURRENT_SOURCE_DIR}/dist.info DIST_INFO ) +if ( "${DIST_INFO}" STREQUAL "" ) + message ( FATAL_ERROR "Failed to load dist.info." ) +endif () +# Reads field `name` from dist.info string `DIST_INFO` into variable `var`. +macro ( _parse_dist_field name var ) + string ( REGEX REPLACE ".*${name}[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" + ${var} "${DIST_INFO}" ) + if ( ${var} STREQUAL DIST_INFO ) + message ( FATAL_ERROR "Failed to extract \"${var}\" from dist.info" ) + endif () +endmacro () +# +_parse_dist_field ( name DIST_NAME ) +_parse_dist_field ( version DIST_VERSION ) +_parse_dist_field ( license DIST_LICENSE ) +_parse_dist_field ( author DIST_AUTHOR ) +_parse_dist_field ( maintainer DIST_MAINTAINER ) +_parse_dist_field ( url DIST_URL ) +_parse_dist_field ( desc DIST_DESC ) +message ( "DIST_NAME: ${DIST_NAME}") +message ( "DIST_VERSION: ${DIST_VERSION}") +message ( "DIST_LICENSE: ${DIST_LICENSE}") +message ( "DIST_AUTHOR: ${DIST_AUTHOR}") +message ( "DIST_MAINTAINER: ${DIST_MAINTAINER}") +message ( "DIST_URL: ${DIST_URL}") +message ( "DIST_DESC: ${DIST_DESC}") +string ( REGEX REPLACE ".*depends[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" + DIST_DEPENDS ${DIST_INFO} ) +if ( DIST_DEPENDS STREQUAL DIST_INFO ) + set ( DIST_DEPENDS "" ) +endif () +message ( "DIST_DEPENDS: ${DIST_DEPENDS}") +## 2DO: Parse DIST_DEPENDS and try to install Dependencies with automatically using externalproject_add + + +## INSTALL DEFAULTS (Relative to CMAKE_INSTALL_PREFIX) +# Primary paths +set ( INSTALL_BIN bin CACHE PATH "Where to install binaries to." ) +set ( INSTALL_LIB lib CACHE PATH "Where to install libraries to." ) +set ( INSTALL_INC include CACHE PATH "Where to install headers to." ) +set ( INSTALL_ETC etc CACHE PATH "Where to store configuration files" ) +set ( INSTALL_SHARE share CACHE PATH "Directory for shared data." ) + +# Secondary paths +option ( INSTALL_VERSION + "Install runtime libraries and executables with version information." OFF) +set ( INSTALL_DATA ${INSTALL_SHARE}/${DIST_NAME} CACHE PATH + "Directory the package can store documentation, tests or other data in.") +set ( INSTALL_DOC ${INSTALL_DATA}/doc CACHE PATH + "Recommended directory to install documentation into.") +set ( INSTALL_EXAMPLE ${INSTALL_DATA}/example CACHE PATH + "Recommended directory to install examples into.") +set ( INSTALL_TEST ${INSTALL_DATA}/test CACHE PATH + "Recommended directory to install tests into.") +set ( INSTALL_FOO ${INSTALL_DATA}/etc CACHE PATH + "Where to install additional files") + +# Tweaks and other defaults +# Setting CMAKE to use loose block and search for find modules in source directory +set ( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true ) +set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH} ) +option ( BUILD_SHARED_LIBS "Build shared libraries" ON ) + +# In MSVC, prevent warnings that can occur when using standard libraries. +if ( MSVC ) + add_definitions ( -D_CRT_SECURE_NO_WARNINGS ) +endif () + +# RPath and relative linking +option ( USE_RPATH "Use relative linking." ON) +if ( USE_RPATH ) + string ( REGEX REPLACE "[^!/]+" ".." UP_DIR ${INSTALL_BIN} ) + set ( CMAKE_SKIP_BUILD_RPATH FALSE CACHE STRING "" FORCE ) + set ( CMAKE_BUILD_WITH_INSTALL_RPATH FALSE CACHE STRING "" FORCE ) + set ( CMAKE_INSTALL_RPATH $ORIGIN/${UP_DIR}/${INSTALL_LIB} + CACHE STRING "" FORCE ) + set ( CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE CACHE STRING "" FORCE ) + set ( CMAKE_INSTALL_NAME_DIR @executable_path/${UP_DIR}/${INSTALL_LIB} + CACHE STRING "" FORCE ) +endif () + +## MACROS +# Parser macro +macro ( parse_arguments prefix arg_names option_names) + set ( DEFAULT_ARGS ) + foreach ( arg_name ${arg_names} ) + set ( ${prefix}_${arg_name} ) + endforeach () + foreach ( option ${option_names} ) + set ( ${prefix}_${option} FALSE ) + endforeach () + + set ( current_arg_name DEFAULT_ARGS ) + set ( current_arg_list ) + foreach ( arg ${ARGN} ) + set ( larg_names ${arg_names} ) + list ( FIND larg_names "${arg}" is_arg_name ) + if ( is_arg_name GREATER -1 ) + set ( ${prefix}_${current_arg_name} ${current_arg_list} ) + set ( current_arg_name ${arg} ) + set ( current_arg_list ) + else () + set ( loption_names ${option_names} ) + list ( FIND loption_names "${arg}" is_option ) + if ( is_option GREATER -1 ) + set ( ${prefix}_${arg} TRUE ) + else () + set ( current_arg_list ${current_arg_list} ${arg} ) + endif () + endif () + endforeach () + set ( ${prefix}_${current_arg_name} ${current_arg_list} ) +endmacro () + + +# install_executable ( executable_targets ) +# Installs any executables generated using "add_executable". +# USE: install_executable ( lua ) +# NOTE: subdirectories are NOT supported +set ( CPACK_COMPONENT_RUNTIME_DISPLAY_NAME "${DIST_NAME} Runtime" ) +set ( CPACK_COMPONENT_RUNTIME_DESCRIPTION + "Executables and runtime libraries. Installed into ${INSTALL_BIN}." ) +macro ( install_executable ) + foreach ( _file ${ARGN} ) + if ( INSTALL_VERSION ) + set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} + SOVERSION ${DIST_VERSION} ) + endif () + install ( TARGETS ${_file} RUNTIME DESTINATION ${INSTALL_BIN} + COMPONENT Runtime ) + endforeach() +endmacro () + +# install_library ( library_targets ) +# Installs any libraries generated using "add_library" into apropriate places. +# USE: install_library ( libexpat ) +# NOTE: subdirectories are NOT supported +set ( CPACK_COMPONENT_LIBRARY_DISPLAY_NAME "${DIST_NAME} Development Libraries" ) +set ( CPACK_COMPONENT_LIBRARY_DESCRIPTION + "Static and import libraries needed for development. Installed into ${INSTALL_LIB} or ${INSTALL_BIN}." ) +macro ( install_library ) + foreach ( _file ${ARGN} ) + if ( INSTALL_VERSION ) + set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} + SOVERSION ${DIST_VERSION} ) + endif () + install ( TARGETS ${_file} + RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT Runtime + LIBRARY DESTINATION ${INSTALL_LIB} COMPONENT Runtime + ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT Library ) + endforeach() +endmacro () + +# helper function for various install_* functions, for PATTERN/REGEX args. +macro ( _complete_install_args ) + if ( NOT("${_ARG_PATTERN}" STREQUAL "") ) + set ( _ARG_PATTERN PATTERN ${_ARG_PATTERN} ) + endif () + if ( NOT("${_ARG_REGEX}" STREQUAL "") ) + set ( _ARG_REGEX REGEX ${_ARG_REGEX} ) + endif () +endmacro () + +# install_header ( files/directories [INTO destination] ) +# Install a directories or files into header destination. +# USE: install_header ( lua.h luaconf.h ) or install_header ( GL ) +# USE: install_header ( mylib.h INTO mylib ) +# For directories, supports optional PATTERN/REGEX arguments like install(). +set ( CPACK_COMPONENT_HEADER_DISPLAY_NAME "${DIST_NAME} Development Headers" ) +set ( CPACK_COMPONENT_HEADER_DESCRIPTION + "Headers needed for development. Installed into ${INSTALL_INC}." ) +macro ( install_header ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} + COMPONENT Header ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} + COMPONENT Header ) + endif () + endforeach() +endmacro () + +# install_data ( files/directories [INTO destination] ) +# This installs additional data files or directories. +# USE: install_data ( extra data.dat ) +# USE: install_data ( image1.png image2.png INTO images ) +# For directories, supports optional PATTERN/REGEX arguments like install(). +set ( CPACK_COMPONENT_DATA_DISPLAY_NAME "${DIST_NAME} Data" ) +set ( CPACK_COMPONENT_DATA_DESCRIPTION + "Application data. Installed into ${INSTALL_DATA}." ) +macro ( install_data ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} + DESTINATION ${INSTALL_DATA}/${_ARG_INTO} + COMPONENT Data ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_DATA}/${_ARG_INTO} + COMPONENT Data ) + endif () + endforeach() +endmacro () + +# INSTALL_DOC ( files/directories [INTO destination] ) +# This installs documentation content +# USE: install_doc ( doc/ doc.pdf ) +# USE: install_doc ( index.html INTO html ) +# For directories, supports optional PATTERN/REGEX arguments like install(). +set ( CPACK_COMPONENT_DOCUMENTATION_DISPLAY_NAME "${DIST_NAME} Documentation" ) +set ( CPACK_COMPONENT_DOCUMENTATION_DESCRIPTION + "Application documentation. Installed into ${INSTALL_DOC}." ) +macro ( install_doc ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} + COMPONENT Documentation ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} + COMPONENT Documentation ) + endif () + endforeach() +endmacro () + +# install_example ( files/directories [INTO destination] ) +# This installs additional examples +# USE: install_example ( examples/ exampleA ) +# USE: install_example ( super_example super_data INTO super) +# For directories, supports optional PATTERN/REGEX argument like install(). +set ( CPACK_COMPONENT_EXAMPLE_DISPLAY_NAME "${DIST_NAME} Examples" ) +set ( CPACK_COMPONENT_EXAMPLE_DESCRIPTION + "Examples and their associated data. Installed into ${INSTALL_EXAMPLE}." ) +macro ( install_example ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} + COMPONENT Example ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} + COMPONENT Example ) + endif () + endforeach() +endmacro () + +# install_test ( files/directories [INTO destination] ) +# This installs tests and test files, DOES NOT EXECUTE TESTS +# USE: install_test ( my_test data.sql ) +# USE: install_test ( feature_x_test INTO x ) +# For directories, supports optional PATTERN/REGEX argument like install(). +set ( CPACK_COMPONENT_TEST_DISPLAY_NAME "${DIST_NAME} Tests" ) +set ( CPACK_COMPONENT_TEST_DESCRIPTION + "Tests and associated data. Installed into ${INSTALL_TEST}." ) +macro ( install_test ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} + COMPONENT Test ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} + COMPONENT Test ) + endif () + endforeach() +endmacro () + +# install_foo ( files/directories [INTO destination] ) +# This installs optional or otherwise unneeded content +# USE: install_foo ( etc/ example.doc ) +# USE: install_foo ( icon.png logo.png INTO icons) +# For directories, supports optional PATTERN/REGEX argument like install(). +set ( CPACK_COMPONENT_OTHER_DISPLAY_NAME "${DIST_NAME} Unspecified Content" ) +set ( CPACK_COMPONENT_OTHER_DESCRIPTION + "Other unspecified content. Installed into ${INSTALL_FOO}." ) +macro ( install_foo ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} + COMPONENT Other ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} + COMPONENT Other ) + endif () + endforeach() +endmacro () + +## CTest defaults + +## CPack defaults +set ( CPACK_GENERATOR "ZIP" ) +set ( CPACK_STRIP_FILES TRUE ) +set ( CPACK_PACKAGE_NAME "${DIST_NAME}" ) +set ( CPACK_PACKAGE_VERSION "${DIST_VERSION}") +set ( CPACK_PACKAGE_VENDOR "LuaDist" ) +set ( CPACK_COMPONENTS_ALL Runtime Library Header Data Documentation Example Other ) +include ( CPack ) diff --git a/cmake/lua.cmake b/cmake/lua.cmake new file mode 100644 index 0000000..ee2e35d --- /dev/null +++ b/cmake/lua.cmake @@ -0,0 +1,309 @@ +# LuaDist CMake utility library for Lua. +# +# Copyright (C) 2007-2012 LuaDist. +# by David Manura, Peter Drahos +# Redistribution and use of this file is allowed according to the terms of the MIT license. +# For details see the COPYRIGHT file distributed with LuaDist. +# Please note that the package source code is licensed under its own license. + +set ( INSTALL_LMOD ${INSTALL_LIB}/lua + CACHE PATH "Directory to install Lua modules." ) +set ( INSTALL_CMOD ${INSTALL_LIB}/lua + CACHE PATH "Directory to install Lua binary modules." ) + +option ( LUA_SKIP_WRAPPER + "Do not build and install Lua executable wrappers." OFF ) +option ( LUA_STATIC_MODULE "Build modules for static linking" OFF ) + +# List of (Lua module name, file path) pairs. +# Used internally by add_lua_test. Built by add_lua_module. +set ( _lua_modules ) + +# utility function: appends path `path` to path `basepath`, properly +# handling cases when `path` may be relative or absolute. +macro ( _append_path basepath path result ) + if ( IS_ABSOLUTE "${path}" ) + set ( ${result} "${path}" ) + else () + set ( ${result} "${basepath}/${path}" ) + endif () +endmacro () + +# install_lua_executable ( target source ) +# Automatically generate a binary if srlua package is available +# The application or its source will be placed into /bin +# If the application source did not have .lua suffix then it will be added +# USE: lua_executable ( sputnik src/sputnik.lua ) +macro ( install_lua_executable _name _source ) + get_filename_component ( _source_name ${_source} NAME_WE ) + # Find srlua and glue + find_program( SRLUA_EXECUTABLE NAMES srlua ) + find_program( GLUE_EXECUTABLE NAMES glue ) + # Executable output + set ( _exe ${CMAKE_CURRENT_BINARY_DIR}/${_name}${CMAKE_EXECUTABLE_SUFFIX} ) + if ( NOT SKIP_LUA_WRAPPER AND SRLUA_EXECUTABLE AND GLUE_EXECUTABLE ) + # Generate binary gluing the lua code to srlua, this is a robuust approach for most systems + add_custom_command( + OUTPUT ${_exe} + COMMAND ${GLUE_EXECUTABLE} + ARGS ${SRLUA_EXECUTABLE} ${_source} ${_exe} + DEPENDS ${_source} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + VERBATIM + ) + # Make sure we have a target associated with the binary + add_custom_target(${_name} ALL + DEPENDS ${_exe} + ) + # Install with run permissions + install ( PROGRAMS ${_exe} DESTINATION ${INSTALL_BIN} COMPONENT Runtime) + # Also install source as optional resurce + install ( FILES ${_source} DESTINATION ${INSTALL_FOO} COMPONENT Other ) + else() + # Install into bin as is but without the lua suffix, we assume the executable uses UNIX shebang/hash-bang magic + install ( PROGRAMS ${_source} DESTINATION ${INSTALL_BIN} + RENAME ${_source_name} + COMPONENT Runtime + ) + endif() +endmacro () + +macro ( _lua_module_helper is_install _name ) + parse_arguments ( _MODULE "LINK;ALL_IN_ONE" "" ${ARGN} ) + # _target is CMake-compatible target name for module (e.g. socket_core). + # _module is relative path of target (e.g. socket/core), + # without extension (e.g. .lua/.so/.dll). + # _MODULE_SRC is list of module source files (e.g. .lua and .c files). + # _MODULE_NAMES is list of module names (e.g. socket.core). + if ( _MODULE_ALL_IN_ONE ) + string ( REGEX REPLACE "\\..*" "" _target "${_name}" ) + string ( REGEX REPLACE "\\..*" "" _module "${_name}" ) + set ( _target "${_target}_all_in_one") + set ( _MODULE_SRC ${_MODULE_ALL_IN_ONE} ) + set ( _MODULE_NAMES ${_name} ${_MODULE_DEFAULT_ARGS} ) + else () + string ( REPLACE "." "_" _target "${_name}" ) + string ( REPLACE "." "/" _module "${_name}" ) + set ( _MODULE_SRC ${_MODULE_DEFAULT_ARGS} ) + set ( _MODULE_NAMES ${_name} ) + endif () + if ( NOT _MODULE_SRC ) + message ( FATAL_ERROR "no module sources specified" ) + endif () + list ( GET _MODULE_SRC 0 _first_source ) + + get_filename_component ( _ext ${_first_source} EXT ) + if ( _ext STREQUAL ".lua" ) # Lua source module + list ( LENGTH _MODULE_SRC _len ) + if ( _len GREATER 1 ) + message ( FATAL_ERROR "more than one source file specified" ) + endif () + + set ( _module "${_module}.lua" ) + + get_filename_component ( _module_dir ${_module} PATH ) + get_filename_component ( _module_filename ${_module} NAME ) + _append_path ( "${CMAKE_CURRENT_SOURCE_DIR}" "${_first_source}" _module_path ) + list ( APPEND _lua_modules "${_name}" "${_module_path}" ) + + if ( ${is_install} ) + install ( FILES ${_first_source} DESTINATION ${INSTALL_LMOD}/${_module_dir} + RENAME ${_module_filename} + COMPONENT Runtime + ) + endif () + else () # Lua C binary module + enable_language ( C ) + find_package ( Lua REQUIRED ) + include_directories ( ${LUA_INCLUDE_DIR} ) + + set ( _module "${_module}${CMAKE_SHARED_MODULE_SUFFIX}" ) + + get_filename_component ( _module_dir ${_module} PATH ) + get_filename_component ( _module_filenamebase ${_module} NAME_WE ) + foreach ( _thisname ${_MODULE_NAMES} ) + list ( APPEND _lua_modules "${_thisname}" + "${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_CFG_INTDIR}/${_module}" ) + endforeach () + + # Static module (not linking to lua) + if ( LUA_STATIC_MODULE ) + add_library( ${_target} STATIC ${_MODULE_SRC}) + target_link_libraries ( ${_target} ${_MODULE_LINK} ) + else () + # Dynamic module + add_library( ${_target} MODULE ${_MODULE_SRC}) + target_link_libraries ( ${_target} ${LUA_LIBRARY} ${_MODULE_LINK} ) + endif () + + set_target_properties ( ${_target} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${_module_dir}" + LIBRARY_OUTPUT_DIRECTORY "${_module_dir}" + PREFIX "" + OUTPUT_NAME "${_module_filenamebase}" ) + if ( ${is_install} ) + install ( TARGETS ${_target} + LIBRARY DESTINATION ${INSTALL_CMOD}/${_module_dir} + COMPONENT Runtime + ARCHIVE DESTINATION ${INSTALL_CMOD}/${_module_dir} + COMPONENT Library ) + endif () + endif () +endmacro () + +# add_lua_module +# Builds a Lua source module into a destination locatable by Lua +# require syntax. +# Binary modules are also supported where this function takes sources and +# libraries to compile separated by LINK keyword. +# USE: add_lua_module ( socket.http src/http.lua ) +# USE2: add_lua_module ( mime.core src/mime.c ) +# USE3: add_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) +# USE4: add_lua_module ( ssl.context ssl.core ALL_IN_ONE src/context.c src/ssl.c ) +# This form builds an "all-in-one" module (e.g. ssl.so or ssl.dll containing +# both modules ssl.context and ssl.core). The CMake target name will be +# ssl_all_in_one. +# Also sets variable _module_path (relative path where module typically +# would be installed). +macro ( add_lua_module ) + _lua_module_helper ( 0 ${ARGN} ) +endmacro () + + +# install_lua_module +# This is the same as `add_lua_module` but also installs the module. +# USE: install_lua_module ( socket.http src/http.lua ) +# USE2: install_lua_module ( mime.core src/mime.c ) +# USE3: install_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) +macro ( install_lua_module ) + _lua_module_helper ( 1 ${ARGN} ) +endmacro () + +# Builds string representing Lua table mapping Lua modules names to file +# paths. Used internally. +macro ( _make_module_table _outvar ) + set ( ${_outvar} ) + list ( LENGTH _lua_modules _n ) + if ( ${_n} GREATER 0 ) # avoids cmake complaint + foreach ( _i RANGE 1 ${_n} 2 ) + list ( GET _lua_modules ${_i} _path ) + math ( EXPR _ii ${_i}-1 ) + list ( GET _lua_modules ${_ii} _name ) + set ( ${_outvar} "${_table} ['${_name}'] = '${_path}'\;\n") + endforeach () + endif () + set ( ${_outvar} +"local modules = { +${_table}}" ) +endmacro () + +# add_lua_test ( _testfile [ WORKING_DIRECTORY _working_dir ] ) +# Runs Lua script `_testfile` under CTest tester. +# Optional named argument `WORKING_DIRECTORY` is current working directory to +# run test under (defaults to ${CMAKE_CURRENT_BINARY_DIR}). +# Both paths, if relative, are relative to ${CMAKE_CURRENT_SOURCE_DIR}. +# Any modules previously defined with install_lua_module are automatically +# preloaded (via package.preload) prior to running the test script. +# Under LuaDist, set test=true in config.lua to enable testing. +# USE: add_lua_test ( test/test1.lua [args...] [WORKING_DIRECTORY dir]) +macro ( add_lua_test _testfile ) + if ( NOT SKIP_TESTING ) + parse_arguments ( _ARG "WORKING_DIRECTORY" "" ${ARGN} ) + include ( CTest ) + find_program ( LUA NAMES lua lua.bat ) + get_filename_component ( TESTFILEABS ${_testfile} ABSOLUTE ) + get_filename_component ( TESTFILENAME ${_testfile} NAME ) + get_filename_component ( TESTFILEBASE ${_testfile} NAME_WE ) + + # Write wrapper script. + # Note: One simple way to allow the script to find modules is + # to just put them in package.preload. + set ( TESTWRAPPER ${CMAKE_CURRENT_BINARY_DIR}/${TESTFILENAME} ) + _make_module_table ( _table ) + set ( TESTWRAPPERSOURCE +"local CMAKE_CFG_INTDIR = ... or '.' +${_table} +local function preload_modules(modules) + for name, path in pairs(modules) do + if path:match'%.lua' then + package.preload[name] = assert(loadfile(path)) + else + local name = name:gsub('.*%-', '') -- remove any hyphen prefix + local symbol = 'luaopen_' .. name:gsub('%.', '_') + --improve: generalize to support all-in-one loader? + local path = path:gsub('%$%{CMAKE_CFG_INTDIR%}', CMAKE_CFG_INTDIR) + package.preload[name] = assert(package.loadlib(path, symbol)) + end + end +end +preload_modules(modules) +arg[0] = '${TESTFILEABS}' +table.remove(arg, 1) +return assert(loadfile '${TESTFILEABS}')(unpack(arg)) +" ) + if ( _ARG_WORKING_DIRECTORY ) + get_filename_component ( + TESTCURRENTDIRABS ${_ARG_WORKING_DIRECTORY} ABSOLUTE ) + # note: CMake 2.6 (unlike 2.8) lacks WORKING_DIRECTORY parameter. + set ( _pre ${CMAKE_COMMAND} -E chdir "${TESTCURRENTDIRABS}" ) + endif () + file ( WRITE ${TESTWRAPPER} ${TESTWRAPPERSOURCE}) + add_test ( NAME ${TESTFILEBASE} COMMAND ${_pre} ${LUA} + ${TESTWRAPPER} "${CMAKE_CFG_INTDIR}" + ${_ARG_DEFAULT_ARGS} ) + endif () + # see also http://gdcm.svn.sourceforge.net/viewvc/gdcm/Sandbox/CMakeModules/UsePythonTest.cmake + # Note: ${CMAKE_CFG_INTDIR} is a command-line argument to allow proper + # expansion by the native build tool. +endmacro () + + +# Converts Lua source file `_source` to binary string embedded in C source +# file `_target`. Optionally compiles Lua source to byte code (not available +# under LuaJIT2, which doesn't have a bytecode loader). Additionally, Lua +# versions of bin2c [1] and luac [2] may be passed respectively as additional +# arguments. +# +# [1] http://lua-users.org/wiki/BinToCee +# [2] http://lua-users.org/wiki/LuaCompilerInLua +function ( add_lua_bin2c _target _source ) + find_program ( LUA NAMES lua lua.bat ) + execute_process ( COMMAND ${LUA} -e "string.dump(function()end)" + RESULT_VARIABLE _LUA_DUMP_RESULT ERROR_QUIET ) + if ( NOT ${_LUA_DUMP_RESULT} ) + SET ( HAVE_LUA_DUMP true ) + endif () + message ( "-- string.dump=${HAVE_LUA_DUMP}" ) + + if ( ARGV2 ) + get_filename_component ( BIN2C ${ARGV2} ABSOLUTE ) + set ( BIN2C ${LUA} ${BIN2C} ) + else () + find_program ( BIN2C NAMES bin2c bin2c.bat ) + endif () + if ( HAVE_LUA_DUMP ) + if ( ARGV3 ) + get_filename_component ( LUAC ${ARGV3} ABSOLUTE ) + set ( LUAC ${LUA} ${LUAC} ) + else () + find_program ( LUAC NAMES luac luac.bat ) + endif () + endif ( HAVE_LUA_DUMP ) + message ( "-- bin2c=${BIN2C}" ) + message ( "-- luac=${LUAC}" ) + + get_filename_component ( SOURCEABS ${_source} ABSOLUTE ) + if ( HAVE_LUA_DUMP ) + get_filename_component ( SOURCEBASE ${_source} NAME_WE ) + add_custom_command ( + OUTPUT ${_target} DEPENDS ${_source} + COMMAND ${LUAC} -o ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo + ${SOURCEABS} + COMMAND ${BIN2C} ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo + ">${_target}" ) + else () + add_custom_command ( + OUTPUT ${_target} DEPENDS ${SOURCEABS} + COMMAND ${BIN2C} ${_source} ">${_target}" ) + endif () +endfunction() diff --git a/dist.info b/dist.info new file mode 100644 index 0000000..5dfdbaa --- /dev/null +++ b/dist.info @@ -0,0 +1,12 @@ +name = "lua-llthreads2" +version = "0.1.1" + +desc = "A simple Lua wrapper for pthreads & WIN32 threads." +author = "Alexey Melnichuk" +license = "MIT/X11" +url = "https://github.com/moteus/lua-llthreads2" +maintainer = "Alexey Melnichuk" + +depends = { + "lua > 5.1" +} \ No newline at end of file diff --git a/llthreads2/.gitignore b/llthreads2/.gitignore new file mode 100644 index 0000000..2122aa6 --- /dev/null +++ b/llthreads2/.gitignore @@ -0,0 +1,18 @@ +*.d +*.spec +*.obj +*.dll +*.o +*.lib +*.exp +*.suo +*.ncb +*.user +*.pdb +msvc/Debug/ +msvc/Release/ +CMakeCache.txt +CMakeFiles +Makefile +cmake_install.cmake +install_manifest.txt diff --git a/llthreads2/.travis.yml b/llthreads2/.travis.yml new file mode 100644 index 0000000..5b72267 --- /dev/null +++ b/llthreads2/.travis.yml @@ -0,0 +1,42 @@ +language: objective-c + +env: + global: + - PLATFORM=macosx + - LUAROCKS_VER=2.1.0 + matrix: + - LUA=lua5.1 LUA_SFX= + - LUA=lua5.2 LUA_SFX= + - LUA=luajit LUA_SFX=jit + +branches: + only: + - master + +before_install: + - bash .travis/setup_lua.sh + +install: + - sudo luarocks install lunitx + - sudo luarocks make rockspecs/lua-llthreads2-compat-scm-0.rockspec + - sudo luarocks make rockspecs/lua-llthreads2-scm-0.rockspec + +script: + - cd test + - lua$LUA_SFX test_table_copy.lua + - lua$LUA_SFX test_threads.lua + - lua$LUA_SFX test_llthreads.lua + # - lua$LUA_SFX test_register_llthreads.lua + - lua$LUA_SFX test_join_timeout.lua + - lua$LUA_SFX test_join_detach.lua + - lua$LUA_SFX test_join_error.lua + - lua$LUA_SFX test_register_ffi.lua + - lua$LUA_SFX test_logger.lua + - lua$LUA_SFX test_pass_cfunction.lua + - lua$LUA_SFX test_load_llthreads2.lua + - lua$LUA_SFX test_alive.lua + +notifications: + email: + on_success: change + on_failure: always diff --git a/llthreads2/.travis/setup_lua.sh b/llthreads2/.travis/setup_lua.sh new file mode 100644 index 0000000..47ce49c --- /dev/null +++ b/llthreads2/.travis/setup_lua.sh @@ -0,0 +1,35 @@ +# A script for setting up environment for travis-ci testing. +# Sets up Lua and Luarocks. +# LUA must be "lua5.1", "lua5.2" or "luajit". +# PLATFORM must be "linux" or "macosx". + +if [ "$LUA" == "luajit" ]; then + curl http://luajit.org/download/LuaJIT-2.0.2.tar.gz | tar xz + cd LuaJIT-2.0.2 + make && sudo make install + cd $TRAVIS_BUILD_DIR; +else + if [ "$LUA" == "lua5.1" ]; then + curl http://www.lua.org/ftp/lua-5.1.5.tar.gz | tar xz + cd lua-5.1.5; + elif [ "$LUA" == "lua5.2" ]; then + curl http://www.lua.org/ftp/lua-5.2.3.tar.gz | tar xz + cd lua-5.2.3; + fi + sudo make $PLATFORM install + cd $TRAVIS_BUILD_DIR; +fi + +LUAROCKS_BASE=luarocks-$LUAROCKS_VER +curl http://luarocks.org/releases/$LUAROCKS_BASE.tar.gz | tar xz +cd $LUAROCKS_BASE; + +if [ "$LUA" == "luajit" ]; then + ./configure --lua-suffix=jit --with-lua-include=/usr/local/include/luajit-2.0; +else + ./configure; +fi + +make && sudo make install + +cd $TRAVIS_BUILD_DIR \ No newline at end of file diff --git a/llthreads2/CMakeLists.txt b/llthreads2/CMakeLists.txt new file mode 100644 index 0000000..f96b32a --- /dev/null +++ b/llthreads2/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required ( VERSION 2.8 ) + +project ( lua-llthreads2 C ) +include ( cmake/dist.cmake ) +include ( lua ) + +set ( CMAKE_THREAD_PREFER_PTHREAD TRUE ) +include ( FindThreads ) + +include_directories ( ${CMAKE_CURRENT_SOURCE_DIR}/src ) + +set ( LUA_LLTHREADS_SRC src/l52util.c src/llthread.c ) + +install_lua_module ( llthreads2 ${LUA_LLTHREADS_SRC} LINK ${CMAKE_THREAD_LIBS_INIT} ) + +install_data ( COPYRIGHT.llthreads README.md LICENSE ) +install_test ( test/ ) diff --git a/llthreads2/COPYRIGHT.llthreads b/llthreads2/COPYRIGHT.llthreads new file mode 100644 index 0000000..4826006 --- /dev/null +++ b/llthreads2/COPYRIGHT.llthreads @@ -0,0 +1,19 @@ +Copyright (c) 2011 by Robert G. Jakabosky + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/llthreads2/LICENSE b/llthreads2/LICENSE new file mode 100644 index 0000000..484264b --- /dev/null +++ b/llthreads2/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Alexey Melnichuk + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/llthreads2/README.md b/llthreads2/README.md new file mode 100644 index 0000000..22f2025 --- /dev/null +++ b/llthreads2/README.md @@ -0,0 +1,113 @@ +lua-llthreads2 +============== +[![Build Status](https://travis-ci.org/moteus/lua-llthreads2.png?branch=master)](https://travis-ci.org/moteus/lua-llthreads2) +[![Build Status](https://buildhive.cloudbees.com/job/moteus/job/lua-llthreads2/badge/icon)](https://buildhive.cloudbees.com/job/moteus/job/lua-llthreads2/) +[![Build Status](https://moteus.ci.cloudbees.com/job/lua-llthreads2/badge/icon)](https://moteus.ci.cloudbees.com/job/lua-llthreads2/) + +This is full dropin replacement for [llthreads](https://github.com/Neopallium/lua-llthreads) library. + +##Incompatibility list with origin llthreads library +* does not support Lua 5.0 +* does not support ffi interface (use Lua C API for LuaJIT) +* returns nil instead of false on error +* start method returns self instead of true on success + +##Additional +* thread:join() method support zero timeout to check if thread alive (does not work on Windows with pthreads) +* thread:join() method support arbitrary timeout on Windows threads +* thread:alive() method return whether the thread is alive (does not work on Windows with pthreads) +* set_logger function allow logging errors (crash Lua VM) in current llthread's threads +* thread:start() has additional parameter which control in which thread child Lua VM will be destroyed +* allow pass cfunctions to child thread (e.g. to initialize Lua state) + +##Usage + +### Use custom logger +In this example I use [lua-log](https://github.com/moteus/lua-log) library. +``` Lua +-- This is child thread. +local llthreads = require "llthreads" +-- Send logs using ZMQ +local LOG = require"log".new( + require "log.writer.net.zmq".new("tcp://127.0.0.1:5555") +) +llthread.set_logger(function(msg) LOG.error(msg) end) +-- This error with traceback will be passed to logger +error("SOME ERROR") +``` + +### Start attached thread collectd in child thread +``` Lua +-- This is main thread. +local thread = require "llthreads".new[[ + require "utils".sleep(5) +]] + +-- We tell that we start attached thread but child Lua State shuld be close in child thread. +-- If `thread` became garbage in main thread then finallizer calls thread:join() +-- and main thread may hungup. +thread:start(false, false) + +-- We can call join. +-- Because of Lua state destroys in child thread we can not get +-- returned Lua vaules so we just returns `true`. +thread:join() +``` + +### Start detached joinable thread +``` Lua +-- This is main thread. +local thread = require "llthreads".new[[ + require "utils".sleep(5) +]] + +-- We tell that we start detached joinable thread. In fact we start attached +-- thread but if `thread` became garbage in main thread then finallizer just +-- detach child thread and main thread may not hungup. +thread:start(true, true) + +-- We can call join. +-- Because of Lua state destroys in child thread we can not get +-- returned Lua vaules so we just returns `true`. +thread:join() +``` + +### Pass to child thread host application`s library loader +If you close parent Lua state then some dynamic library may be unloaded +and cfunction in child Lua state (thread) became invalid. + +``` Lua +-- `myhost.XXX` modules is built-in modules in host application +-- host application registers cfunction as module loader +local preload = {} +preload[ 'myhost.logger' ] = package.preload[ 'myhost.logger' ] +preload[ 'myhost.config' ] = package.preload[ 'myhost.config' ] +llthreads.new([[ + -- registers preload + local preload = ... + for name, fn in pairs(preload) do package.preload[name] = fn end + + local log = require 'myhost.logger' + +]], preload):start(true) +``` + +### Wait while thread is alive +``` Lua +local thread = require "llthreads".new[[ + require "utils".sleep(5) + return 1 +]] +thread:start() + +-- we can not use `thread:join(0)` because we can not call it twice +-- so all returned vaules will be lost +while thread:alive() do + -- do some work +end + +local ok, ret = thread:join() -- true, 1 +``` + +[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/moteus/lua-llthreads2/trend.png)](https://bitdeli.com/free "Bitdeli Badge") + diff --git a/llthreads2/cmake/FindLua.cmake b/llthreads2/cmake/FindLua.cmake new file mode 100644 index 0000000..6991b4a --- /dev/null +++ b/llthreads2/cmake/FindLua.cmake @@ -0,0 +1,127 @@ +# Locate Lua library +# This module defines +# LUA_EXECUTABLE, if found +# LUA_FOUND, if false, do not try to link to Lua +# LUA_LIBRARIES +# LUA_INCLUDE_DIR, where to find lua.h +# LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8) +# +# Note that the expected include convention is +# #include "lua.h" +# and not +# #include +# This is because, the lua location is not standardized and may exist +# in locations other than lua/ + +#============================================================================= +# Copyright 2007-2009 Kitware, Inc. +# Modified to support Lua 5.2 by LuaDist 2012 +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) +# +# The required version of Lua can be specified using the +# standard syntax, e.g. FIND_PACKAGE(Lua 5.1) +# Otherwise the module will search for any available Lua implementation + +# Always search for non-versioned lua first (recommended) +SET(_POSSIBLE_LUA_INCLUDE include include/lua) +SET(_POSSIBLE_LUA_EXECUTABLE lua) +SET(_POSSIBLE_LUA_LIBRARY lua) + +# Determine possible naming suffixes (there is no standard for this) +IF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) + SET(_POSSIBLE_SUFFIXES "${Lua_FIND_VERSION_MAJOR}${Lua_FIND_VERSION_MINOR}" "${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}" "-${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}") +ELSE(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) + SET(_POSSIBLE_SUFFIXES "52" "5.2" "-5.2" "51" "5.1" "-5.1") +ENDIF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) + +# Set up possible search names and locations +FOREACH(_SUFFIX ${_POSSIBLE_SUFFIXES}) + LIST(APPEND _POSSIBLE_LUA_INCLUDE "include/lua${_SUFFIX}") + LIST(APPEND _POSSIBLE_LUA_EXECUTABLE "lua${_SUFFIX}") + LIST(APPEND _POSSIBLE_LUA_LIBRARY "lua${_SUFFIX}") +ENDFOREACH(_SUFFIX) + +# Find the lua executable +FIND_PROGRAM(LUA_EXECUTABLE + NAMES ${_POSSIBLE_LUA_EXECUTABLE} +) + +# Find the lua header +FIND_PATH(LUA_INCLUDE_DIR lua.h + HINTS + $ENV{LUA_DIR} + PATH_SUFFIXES ${_POSSIBLE_LUA_INCLUDE} + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) + +# Find the lua library +FIND_LIBRARY(LUA_LIBRARY + NAMES ${_POSSIBLE_LUA_LIBRARY} + HINTS + $ENV{LUA_DIR} + PATH_SUFFIXES lib64 lib + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw + /opt/local + /opt/csw + /opt +) + +IF(LUA_LIBRARY) + # include the math library for Unix + IF(UNIX AND NOT APPLE) + FIND_LIBRARY(LUA_MATH_LIBRARY m) + SET( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") + # For Windows and Mac, don't need to explicitly include the math library + ELSE(UNIX AND NOT APPLE) + SET( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") + ENDIF(UNIX AND NOT APPLE) +ENDIF(LUA_LIBRARY) + +# Determine Lua version +IF(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") + FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua .+\"") + + STRING(REGEX REPLACE "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([^\"]+)\".*" "\\1" LUA_VERSION_STRING "${lua_version_str}") + UNSET(lua_version_str) +ENDIF() + +# Lua 5.2 +IF(NOT LUA_VERSION_STRING) + FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define LUA_VERSION_[A-Z]+[ \t]+\"[0-9]+\"") + STRING(REGEX REPLACE ".*#define LUA_VERSION_MAJOR[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_MAJOR ${lua_version_str}) + STRING(REGEX REPLACE ".*#define LUA_VERSION_MINOR[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_MINOR ${lua_version_str}) + STRING(REGEX REPLACE ".*#define LUA_VERSION_RELEASE[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_RELEASE ${lua_version_str}) + SET(LUA_VERSION_STRING ${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_RELEASE}) +ENDIF() + +INCLUDE(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if +# all listed variables are TRUE +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua + REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR + VERSION_VAR LUA_VERSION_STRING) + +MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY LUA_EXECUTABLE) + diff --git a/llthreads2/cmake/dist.cmake b/llthreads2/cmake/dist.cmake new file mode 100644 index 0000000..310ef94 --- /dev/null +++ b/llthreads2/cmake/dist.cmake @@ -0,0 +1,321 @@ +# LuaDist CMake utility library. +# Provides sane project defaults and macros common to LuaDist CMake builds. +# +# Copyright (C) 2007-2012 LuaDist. +# by David Manura, Peter Drahoš +# Redistribution and use of this file is allowed according to the terms of the MIT license. +# For details see the COPYRIGHT file distributed with LuaDist. +# Please note that the package source code is licensed under its own license. + +## Extract information from dist.info +if ( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/dist.info ) + message ( FATAL_ERROR + "Missing dist.info file (${CMAKE_CURRENT_SOURCE_DIR}/dist.info)." ) +endif () +file ( READ ${CMAKE_CURRENT_SOURCE_DIR}/dist.info DIST_INFO ) +if ( "${DIST_INFO}" STREQUAL "" ) + message ( FATAL_ERROR "Failed to load dist.info." ) +endif () +# Reads field `name` from dist.info string `DIST_INFO` into variable `var`. +macro ( _parse_dist_field name var ) + string ( REGEX REPLACE ".*${name}[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" + ${var} "${DIST_INFO}" ) + if ( ${var} STREQUAL DIST_INFO ) + message ( FATAL_ERROR "Failed to extract \"${var}\" from dist.info" ) + endif () +endmacro () +# +_parse_dist_field ( name DIST_NAME ) +_parse_dist_field ( version DIST_VERSION ) +_parse_dist_field ( license DIST_LICENSE ) +_parse_dist_field ( author DIST_AUTHOR ) +_parse_dist_field ( maintainer DIST_MAINTAINER ) +_parse_dist_field ( url DIST_URL ) +_parse_dist_field ( desc DIST_DESC ) +message ( "DIST_NAME: ${DIST_NAME}") +message ( "DIST_VERSION: ${DIST_VERSION}") +message ( "DIST_LICENSE: ${DIST_LICENSE}") +message ( "DIST_AUTHOR: ${DIST_AUTHOR}") +message ( "DIST_MAINTAINER: ${DIST_MAINTAINER}") +message ( "DIST_URL: ${DIST_URL}") +message ( "DIST_DESC: ${DIST_DESC}") +string ( REGEX REPLACE ".*depends[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" + DIST_DEPENDS ${DIST_INFO} ) +if ( DIST_DEPENDS STREQUAL DIST_INFO ) + set ( DIST_DEPENDS "" ) +endif () +message ( "DIST_DEPENDS: ${DIST_DEPENDS}") +## 2DO: Parse DIST_DEPENDS and try to install Dependencies with automatically using externalproject_add + + +## INSTALL DEFAULTS (Relative to CMAKE_INSTALL_PREFIX) +# Primary paths +set ( INSTALL_BIN bin CACHE PATH "Where to install binaries to." ) +set ( INSTALL_LIB lib CACHE PATH "Where to install libraries to." ) +set ( INSTALL_INC include CACHE PATH "Where to install headers to." ) +set ( INSTALL_ETC etc CACHE PATH "Where to store configuration files" ) +set ( INSTALL_SHARE share CACHE PATH "Directory for shared data." ) + +# Secondary paths +option ( INSTALL_VERSION + "Install runtime libraries and executables with version information." OFF) +set ( INSTALL_DATA ${INSTALL_SHARE}/${DIST_NAME} CACHE PATH + "Directory the package can store documentation, tests or other data in.") +set ( INSTALL_DOC ${INSTALL_DATA}/doc CACHE PATH + "Recommended directory to install documentation into.") +set ( INSTALL_EXAMPLE ${INSTALL_DATA}/example CACHE PATH + "Recommended directory to install examples into.") +set ( INSTALL_TEST ${INSTALL_DATA}/test CACHE PATH + "Recommended directory to install tests into.") +set ( INSTALL_FOO ${INSTALL_DATA}/etc CACHE PATH + "Where to install additional files") + +# Tweaks and other defaults +# Setting CMAKE to use loose block and search for find modules in source directory +set ( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true ) +set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH} ) +option ( BUILD_SHARED_LIBS "Build shared libraries" ON ) + +# In MSVC, prevent warnings that can occur when using standard libraries. +if ( MSVC ) + add_definitions ( -D_CRT_SECURE_NO_WARNINGS ) +endif () + +# RPath and relative linking +option ( USE_RPATH "Use relative linking." ON) +if ( USE_RPATH ) + string ( REGEX REPLACE "[^!/]+" ".." UP_DIR ${INSTALL_BIN} ) + set ( CMAKE_SKIP_BUILD_RPATH FALSE CACHE STRING "" FORCE ) + set ( CMAKE_BUILD_WITH_INSTALL_RPATH FALSE CACHE STRING "" FORCE ) + set ( CMAKE_INSTALL_RPATH $ORIGIN/${UP_DIR}/${INSTALL_LIB} + CACHE STRING "" FORCE ) + set ( CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE CACHE STRING "" FORCE ) + set ( CMAKE_INSTALL_NAME_DIR @executable_path/${UP_DIR}/${INSTALL_LIB} + CACHE STRING "" FORCE ) +endif () + +## MACROS +# Parser macro +macro ( parse_arguments prefix arg_names option_names) + set ( DEFAULT_ARGS ) + foreach ( arg_name ${arg_names} ) + set ( ${prefix}_${arg_name} ) + endforeach () + foreach ( option ${option_names} ) + set ( ${prefix}_${option} FALSE ) + endforeach () + + set ( current_arg_name DEFAULT_ARGS ) + set ( current_arg_list ) + foreach ( arg ${ARGN} ) + set ( larg_names ${arg_names} ) + list ( FIND larg_names "${arg}" is_arg_name ) + if ( is_arg_name GREATER -1 ) + set ( ${prefix}_${current_arg_name} ${current_arg_list} ) + set ( current_arg_name ${arg} ) + set ( current_arg_list ) + else () + set ( loption_names ${option_names} ) + list ( FIND loption_names "${arg}" is_option ) + if ( is_option GREATER -1 ) + set ( ${prefix}_${arg} TRUE ) + else () + set ( current_arg_list ${current_arg_list} ${arg} ) + endif () + endif () + endforeach () + set ( ${prefix}_${current_arg_name} ${current_arg_list} ) +endmacro () + + +# install_executable ( executable_targets ) +# Installs any executables generated using "add_executable". +# USE: install_executable ( lua ) +# NOTE: subdirectories are NOT supported +set ( CPACK_COMPONENT_RUNTIME_DISPLAY_NAME "${DIST_NAME} Runtime" ) +set ( CPACK_COMPONENT_RUNTIME_DESCRIPTION + "Executables and runtime libraries. Installed into ${INSTALL_BIN}." ) +macro ( install_executable ) + foreach ( _file ${ARGN} ) + if ( INSTALL_VERSION ) + set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} + SOVERSION ${DIST_VERSION} ) + endif () + install ( TARGETS ${_file} RUNTIME DESTINATION ${INSTALL_BIN} + COMPONENT Runtime ) + endforeach() +endmacro () + +# install_library ( library_targets ) +# Installs any libraries generated using "add_library" into apropriate places. +# USE: install_library ( libexpat ) +# NOTE: subdirectories are NOT supported +set ( CPACK_COMPONENT_LIBRARY_DISPLAY_NAME "${DIST_NAME} Development Libraries" ) +set ( CPACK_COMPONENT_LIBRARY_DESCRIPTION + "Static and import libraries needed for development. Installed into ${INSTALL_LIB} or ${INSTALL_BIN}." ) +macro ( install_library ) + foreach ( _file ${ARGN} ) + if ( INSTALL_VERSION ) + set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} + SOVERSION ${DIST_VERSION} ) + endif () + install ( TARGETS ${_file} + RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT Runtime + LIBRARY DESTINATION ${INSTALL_LIB} COMPONENT Runtime + ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT Library ) + endforeach() +endmacro () + +# helper function for various install_* functions, for PATTERN/REGEX args. +macro ( _complete_install_args ) + if ( NOT("${_ARG_PATTERN}" STREQUAL "") ) + set ( _ARG_PATTERN PATTERN ${_ARG_PATTERN} ) + endif () + if ( NOT("${_ARG_REGEX}" STREQUAL "") ) + set ( _ARG_REGEX REGEX ${_ARG_REGEX} ) + endif () +endmacro () + +# install_header ( files/directories [INTO destination] ) +# Install a directories or files into header destination. +# USE: install_header ( lua.h luaconf.h ) or install_header ( GL ) +# USE: install_header ( mylib.h INTO mylib ) +# For directories, supports optional PATTERN/REGEX arguments like install(). +set ( CPACK_COMPONENT_HEADER_DISPLAY_NAME "${DIST_NAME} Development Headers" ) +set ( CPACK_COMPONENT_HEADER_DESCRIPTION + "Headers needed for development. Installed into ${INSTALL_INC}." ) +macro ( install_header ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} + COMPONENT Header ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} + COMPONENT Header ) + endif () + endforeach() +endmacro () + +# install_data ( files/directories [INTO destination] ) +# This installs additional data files or directories. +# USE: install_data ( extra data.dat ) +# USE: install_data ( image1.png image2.png INTO images ) +# For directories, supports optional PATTERN/REGEX arguments like install(). +set ( CPACK_COMPONENT_DATA_DISPLAY_NAME "${DIST_NAME} Data" ) +set ( CPACK_COMPONENT_DATA_DESCRIPTION + "Application data. Installed into ${INSTALL_DATA}." ) +macro ( install_data ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} + DESTINATION ${INSTALL_DATA}/${_ARG_INTO} + COMPONENT Data ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_DATA}/${_ARG_INTO} + COMPONENT Data ) + endif () + endforeach() +endmacro () + +# INSTALL_DOC ( files/directories [INTO destination] ) +# This installs documentation content +# USE: install_doc ( doc/ doc.pdf ) +# USE: install_doc ( index.html INTO html ) +# For directories, supports optional PATTERN/REGEX arguments like install(). +set ( CPACK_COMPONENT_DOCUMENTATION_DISPLAY_NAME "${DIST_NAME} Documentation" ) +set ( CPACK_COMPONENT_DOCUMENTATION_DESCRIPTION + "Application documentation. Installed into ${INSTALL_DOC}." ) +macro ( install_doc ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} + COMPONENT Documentation ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} + COMPONENT Documentation ) + endif () + endforeach() +endmacro () + +# install_example ( files/directories [INTO destination] ) +# This installs additional examples +# USE: install_example ( examples/ exampleA ) +# USE: install_example ( super_example super_data INTO super) +# For directories, supports optional PATTERN/REGEX argument like install(). +set ( CPACK_COMPONENT_EXAMPLE_DISPLAY_NAME "${DIST_NAME} Examples" ) +set ( CPACK_COMPONENT_EXAMPLE_DESCRIPTION + "Examples and their associated data. Installed into ${INSTALL_EXAMPLE}." ) +macro ( install_example ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} + COMPONENT Example ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} + COMPONENT Example ) + endif () + endforeach() +endmacro () + +# install_test ( files/directories [INTO destination] ) +# This installs tests and test files, DOES NOT EXECUTE TESTS +# USE: install_test ( my_test data.sql ) +# USE: install_test ( feature_x_test INTO x ) +# For directories, supports optional PATTERN/REGEX argument like install(). +set ( CPACK_COMPONENT_TEST_DISPLAY_NAME "${DIST_NAME} Tests" ) +set ( CPACK_COMPONENT_TEST_DESCRIPTION + "Tests and associated data. Installed into ${INSTALL_TEST}." ) +macro ( install_test ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} + COMPONENT Test ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} + COMPONENT Test ) + endif () + endforeach() +endmacro () + +# install_foo ( files/directories [INTO destination] ) +# This installs optional or otherwise unneeded content +# USE: install_foo ( etc/ example.doc ) +# USE: install_foo ( icon.png logo.png INTO icons) +# For directories, supports optional PATTERN/REGEX argument like install(). +set ( CPACK_COMPONENT_OTHER_DISPLAY_NAME "${DIST_NAME} Unspecified Content" ) +set ( CPACK_COMPONENT_OTHER_DESCRIPTION + "Other unspecified content. Installed into ${INSTALL_FOO}." ) +macro ( install_foo ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} + COMPONENT Other ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} + COMPONENT Other ) + endif () + endforeach() +endmacro () + +## CTest defaults + +## CPack defaults +set ( CPACK_GENERATOR "ZIP" ) +set ( CPACK_STRIP_FILES TRUE ) +set ( CPACK_PACKAGE_NAME "${DIST_NAME}" ) +set ( CPACK_PACKAGE_VERSION "${DIST_VERSION}") +set ( CPACK_PACKAGE_VENDOR "LuaDist" ) +set ( CPACK_COMPONENTS_ALL Runtime Library Header Data Documentation Example Other ) +include ( CPack ) diff --git a/llthreads2/cmake/lua.cmake b/llthreads2/cmake/lua.cmake new file mode 100644 index 0000000..ee2e35d --- /dev/null +++ b/llthreads2/cmake/lua.cmake @@ -0,0 +1,309 @@ +# LuaDist CMake utility library for Lua. +# +# Copyright (C) 2007-2012 LuaDist. +# by David Manura, Peter Drahos +# Redistribution and use of this file is allowed according to the terms of the MIT license. +# For details see the COPYRIGHT file distributed with LuaDist. +# Please note that the package source code is licensed under its own license. + +set ( INSTALL_LMOD ${INSTALL_LIB}/lua + CACHE PATH "Directory to install Lua modules." ) +set ( INSTALL_CMOD ${INSTALL_LIB}/lua + CACHE PATH "Directory to install Lua binary modules." ) + +option ( LUA_SKIP_WRAPPER + "Do not build and install Lua executable wrappers." OFF ) +option ( LUA_STATIC_MODULE "Build modules for static linking" OFF ) + +# List of (Lua module name, file path) pairs. +# Used internally by add_lua_test. Built by add_lua_module. +set ( _lua_modules ) + +# utility function: appends path `path` to path `basepath`, properly +# handling cases when `path` may be relative or absolute. +macro ( _append_path basepath path result ) + if ( IS_ABSOLUTE "${path}" ) + set ( ${result} "${path}" ) + else () + set ( ${result} "${basepath}/${path}" ) + endif () +endmacro () + +# install_lua_executable ( target source ) +# Automatically generate a binary if srlua package is available +# The application or its source will be placed into /bin +# If the application source did not have .lua suffix then it will be added +# USE: lua_executable ( sputnik src/sputnik.lua ) +macro ( install_lua_executable _name _source ) + get_filename_component ( _source_name ${_source} NAME_WE ) + # Find srlua and glue + find_program( SRLUA_EXECUTABLE NAMES srlua ) + find_program( GLUE_EXECUTABLE NAMES glue ) + # Executable output + set ( _exe ${CMAKE_CURRENT_BINARY_DIR}/${_name}${CMAKE_EXECUTABLE_SUFFIX} ) + if ( NOT SKIP_LUA_WRAPPER AND SRLUA_EXECUTABLE AND GLUE_EXECUTABLE ) + # Generate binary gluing the lua code to srlua, this is a robuust approach for most systems + add_custom_command( + OUTPUT ${_exe} + COMMAND ${GLUE_EXECUTABLE} + ARGS ${SRLUA_EXECUTABLE} ${_source} ${_exe} + DEPENDS ${_source} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + VERBATIM + ) + # Make sure we have a target associated with the binary + add_custom_target(${_name} ALL + DEPENDS ${_exe} + ) + # Install with run permissions + install ( PROGRAMS ${_exe} DESTINATION ${INSTALL_BIN} COMPONENT Runtime) + # Also install source as optional resurce + install ( FILES ${_source} DESTINATION ${INSTALL_FOO} COMPONENT Other ) + else() + # Install into bin as is but without the lua suffix, we assume the executable uses UNIX shebang/hash-bang magic + install ( PROGRAMS ${_source} DESTINATION ${INSTALL_BIN} + RENAME ${_source_name} + COMPONENT Runtime + ) + endif() +endmacro () + +macro ( _lua_module_helper is_install _name ) + parse_arguments ( _MODULE "LINK;ALL_IN_ONE" "" ${ARGN} ) + # _target is CMake-compatible target name for module (e.g. socket_core). + # _module is relative path of target (e.g. socket/core), + # without extension (e.g. .lua/.so/.dll). + # _MODULE_SRC is list of module source files (e.g. .lua and .c files). + # _MODULE_NAMES is list of module names (e.g. socket.core). + if ( _MODULE_ALL_IN_ONE ) + string ( REGEX REPLACE "\\..*" "" _target "${_name}" ) + string ( REGEX REPLACE "\\..*" "" _module "${_name}" ) + set ( _target "${_target}_all_in_one") + set ( _MODULE_SRC ${_MODULE_ALL_IN_ONE} ) + set ( _MODULE_NAMES ${_name} ${_MODULE_DEFAULT_ARGS} ) + else () + string ( REPLACE "." "_" _target "${_name}" ) + string ( REPLACE "." "/" _module "${_name}" ) + set ( _MODULE_SRC ${_MODULE_DEFAULT_ARGS} ) + set ( _MODULE_NAMES ${_name} ) + endif () + if ( NOT _MODULE_SRC ) + message ( FATAL_ERROR "no module sources specified" ) + endif () + list ( GET _MODULE_SRC 0 _first_source ) + + get_filename_component ( _ext ${_first_source} EXT ) + if ( _ext STREQUAL ".lua" ) # Lua source module + list ( LENGTH _MODULE_SRC _len ) + if ( _len GREATER 1 ) + message ( FATAL_ERROR "more than one source file specified" ) + endif () + + set ( _module "${_module}.lua" ) + + get_filename_component ( _module_dir ${_module} PATH ) + get_filename_component ( _module_filename ${_module} NAME ) + _append_path ( "${CMAKE_CURRENT_SOURCE_DIR}" "${_first_source}" _module_path ) + list ( APPEND _lua_modules "${_name}" "${_module_path}" ) + + if ( ${is_install} ) + install ( FILES ${_first_source} DESTINATION ${INSTALL_LMOD}/${_module_dir} + RENAME ${_module_filename} + COMPONENT Runtime + ) + endif () + else () # Lua C binary module + enable_language ( C ) + find_package ( Lua REQUIRED ) + include_directories ( ${LUA_INCLUDE_DIR} ) + + set ( _module "${_module}${CMAKE_SHARED_MODULE_SUFFIX}" ) + + get_filename_component ( _module_dir ${_module} PATH ) + get_filename_component ( _module_filenamebase ${_module} NAME_WE ) + foreach ( _thisname ${_MODULE_NAMES} ) + list ( APPEND _lua_modules "${_thisname}" + "${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_CFG_INTDIR}/${_module}" ) + endforeach () + + # Static module (not linking to lua) + if ( LUA_STATIC_MODULE ) + add_library( ${_target} STATIC ${_MODULE_SRC}) + target_link_libraries ( ${_target} ${_MODULE_LINK} ) + else () + # Dynamic module + add_library( ${_target} MODULE ${_MODULE_SRC}) + target_link_libraries ( ${_target} ${LUA_LIBRARY} ${_MODULE_LINK} ) + endif () + + set_target_properties ( ${_target} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${_module_dir}" + LIBRARY_OUTPUT_DIRECTORY "${_module_dir}" + PREFIX "" + OUTPUT_NAME "${_module_filenamebase}" ) + if ( ${is_install} ) + install ( TARGETS ${_target} + LIBRARY DESTINATION ${INSTALL_CMOD}/${_module_dir} + COMPONENT Runtime + ARCHIVE DESTINATION ${INSTALL_CMOD}/${_module_dir} + COMPONENT Library ) + endif () + endif () +endmacro () + +# add_lua_module +# Builds a Lua source module into a destination locatable by Lua +# require syntax. +# Binary modules are also supported where this function takes sources and +# libraries to compile separated by LINK keyword. +# USE: add_lua_module ( socket.http src/http.lua ) +# USE2: add_lua_module ( mime.core src/mime.c ) +# USE3: add_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) +# USE4: add_lua_module ( ssl.context ssl.core ALL_IN_ONE src/context.c src/ssl.c ) +# This form builds an "all-in-one" module (e.g. ssl.so or ssl.dll containing +# both modules ssl.context and ssl.core). The CMake target name will be +# ssl_all_in_one. +# Also sets variable _module_path (relative path where module typically +# would be installed). +macro ( add_lua_module ) + _lua_module_helper ( 0 ${ARGN} ) +endmacro () + + +# install_lua_module +# This is the same as `add_lua_module` but also installs the module. +# USE: install_lua_module ( socket.http src/http.lua ) +# USE2: install_lua_module ( mime.core src/mime.c ) +# USE3: install_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) +macro ( install_lua_module ) + _lua_module_helper ( 1 ${ARGN} ) +endmacro () + +# Builds string representing Lua table mapping Lua modules names to file +# paths. Used internally. +macro ( _make_module_table _outvar ) + set ( ${_outvar} ) + list ( LENGTH _lua_modules _n ) + if ( ${_n} GREATER 0 ) # avoids cmake complaint + foreach ( _i RANGE 1 ${_n} 2 ) + list ( GET _lua_modules ${_i} _path ) + math ( EXPR _ii ${_i}-1 ) + list ( GET _lua_modules ${_ii} _name ) + set ( ${_outvar} "${_table} ['${_name}'] = '${_path}'\;\n") + endforeach () + endif () + set ( ${_outvar} +"local modules = { +${_table}}" ) +endmacro () + +# add_lua_test ( _testfile [ WORKING_DIRECTORY _working_dir ] ) +# Runs Lua script `_testfile` under CTest tester. +# Optional named argument `WORKING_DIRECTORY` is current working directory to +# run test under (defaults to ${CMAKE_CURRENT_BINARY_DIR}). +# Both paths, if relative, are relative to ${CMAKE_CURRENT_SOURCE_DIR}. +# Any modules previously defined with install_lua_module are automatically +# preloaded (via package.preload) prior to running the test script. +# Under LuaDist, set test=true in config.lua to enable testing. +# USE: add_lua_test ( test/test1.lua [args...] [WORKING_DIRECTORY dir]) +macro ( add_lua_test _testfile ) + if ( NOT SKIP_TESTING ) + parse_arguments ( _ARG "WORKING_DIRECTORY" "" ${ARGN} ) + include ( CTest ) + find_program ( LUA NAMES lua lua.bat ) + get_filename_component ( TESTFILEABS ${_testfile} ABSOLUTE ) + get_filename_component ( TESTFILENAME ${_testfile} NAME ) + get_filename_component ( TESTFILEBASE ${_testfile} NAME_WE ) + + # Write wrapper script. + # Note: One simple way to allow the script to find modules is + # to just put them in package.preload. + set ( TESTWRAPPER ${CMAKE_CURRENT_BINARY_DIR}/${TESTFILENAME} ) + _make_module_table ( _table ) + set ( TESTWRAPPERSOURCE +"local CMAKE_CFG_INTDIR = ... or '.' +${_table} +local function preload_modules(modules) + for name, path in pairs(modules) do + if path:match'%.lua' then + package.preload[name] = assert(loadfile(path)) + else + local name = name:gsub('.*%-', '') -- remove any hyphen prefix + local symbol = 'luaopen_' .. name:gsub('%.', '_') + --improve: generalize to support all-in-one loader? + local path = path:gsub('%$%{CMAKE_CFG_INTDIR%}', CMAKE_CFG_INTDIR) + package.preload[name] = assert(package.loadlib(path, symbol)) + end + end +end +preload_modules(modules) +arg[0] = '${TESTFILEABS}' +table.remove(arg, 1) +return assert(loadfile '${TESTFILEABS}')(unpack(arg)) +" ) + if ( _ARG_WORKING_DIRECTORY ) + get_filename_component ( + TESTCURRENTDIRABS ${_ARG_WORKING_DIRECTORY} ABSOLUTE ) + # note: CMake 2.6 (unlike 2.8) lacks WORKING_DIRECTORY parameter. + set ( _pre ${CMAKE_COMMAND} -E chdir "${TESTCURRENTDIRABS}" ) + endif () + file ( WRITE ${TESTWRAPPER} ${TESTWRAPPERSOURCE}) + add_test ( NAME ${TESTFILEBASE} COMMAND ${_pre} ${LUA} + ${TESTWRAPPER} "${CMAKE_CFG_INTDIR}" + ${_ARG_DEFAULT_ARGS} ) + endif () + # see also http://gdcm.svn.sourceforge.net/viewvc/gdcm/Sandbox/CMakeModules/UsePythonTest.cmake + # Note: ${CMAKE_CFG_INTDIR} is a command-line argument to allow proper + # expansion by the native build tool. +endmacro () + + +# Converts Lua source file `_source` to binary string embedded in C source +# file `_target`. Optionally compiles Lua source to byte code (not available +# under LuaJIT2, which doesn't have a bytecode loader). Additionally, Lua +# versions of bin2c [1] and luac [2] may be passed respectively as additional +# arguments. +# +# [1] http://lua-users.org/wiki/BinToCee +# [2] http://lua-users.org/wiki/LuaCompilerInLua +function ( add_lua_bin2c _target _source ) + find_program ( LUA NAMES lua lua.bat ) + execute_process ( COMMAND ${LUA} -e "string.dump(function()end)" + RESULT_VARIABLE _LUA_DUMP_RESULT ERROR_QUIET ) + if ( NOT ${_LUA_DUMP_RESULT} ) + SET ( HAVE_LUA_DUMP true ) + endif () + message ( "-- string.dump=${HAVE_LUA_DUMP}" ) + + if ( ARGV2 ) + get_filename_component ( BIN2C ${ARGV2} ABSOLUTE ) + set ( BIN2C ${LUA} ${BIN2C} ) + else () + find_program ( BIN2C NAMES bin2c bin2c.bat ) + endif () + if ( HAVE_LUA_DUMP ) + if ( ARGV3 ) + get_filename_component ( LUAC ${ARGV3} ABSOLUTE ) + set ( LUAC ${LUA} ${LUAC} ) + else () + find_program ( LUAC NAMES luac luac.bat ) + endif () + endif ( HAVE_LUA_DUMP ) + message ( "-- bin2c=${BIN2C}" ) + message ( "-- luac=${LUAC}" ) + + get_filename_component ( SOURCEABS ${_source} ABSOLUTE ) + if ( HAVE_LUA_DUMP ) + get_filename_component ( SOURCEBASE ${_source} NAME_WE ) + add_custom_command ( + OUTPUT ${_target} DEPENDS ${_source} + COMMAND ${LUAC} -o ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo + ${SOURCEABS} + COMMAND ${BIN2C} ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo + ">${_target}" ) + else () + add_custom_command ( + OUTPUT ${_target} DEPENDS ${SOURCEABS} + COMMAND ${BIN2C} ${_source} ">${_target}" ) + endif () +endfunction() diff --git a/llthreads2/dist.info b/llthreads2/dist.info new file mode 100644 index 0000000..5dfdbaa --- /dev/null +++ b/llthreads2/dist.info @@ -0,0 +1,12 @@ +name = "lua-llthreads2" +version = "0.1.1" + +desc = "A simple Lua wrapper for pthreads & WIN32 threads." +author = "Alexey Melnichuk" +license = "MIT/X11" +url = "https://github.com/moteus/lua-llthreads2" +maintainer = "Alexey Melnichuk" + +depends = { + "lua > 5.1" +} \ No newline at end of file diff --git a/llthreads2/lakeconfig.lua b/llthreads2/lakeconfig.lua new file mode 100644 index 0000000..ffe5ad7 --- /dev/null +++ b/llthreads2/lakeconfig.lua @@ -0,0 +1,252 @@ +local io = require "io" +io.stdout:setvbuf"no" +io.stderr:setvbuf"no" + +function vc_version() + local VER = lake.compiler_version() + MSVC_VER = ({ + [15] = '9'; + [16] = '10'; + })[VER.MAJOR] or '' + return MSVC_VER +end + +if not L then + +local function arkey(t) + assert(type(t) == 'table') + local keys = {} + for k in pairs(t) do + assert(type(k) == 'number') + table.insert(keys, k) + end + table.sort(keys) + return keys +end + +local function ikeys(t) + local keys = arkey(t) + local i = 0 + return function() + i = i + 1 + local k = keys[i] + if k == nil then return end + return k, t[k] + end +end + +local function expand(arr, t) + if t == nil then return arr end + + if type(t) ~= 'table' then + table.insert(arr, t) + return arr + end + + for _, v in ikeys(t) do + expand(arr, v) + end + + return arr +end + +function L(...) + return expand({}, {...}) +end + +end + +J = J or path.join + +IF = IF or lake.choose or choose + +DIR_SEP = package.config:sub(1,1) + +function prequire(...) + local ok, mod = pcall(require, ...) + if ok then return mod end +end + +function clone(t, o) + o = o or {} + for k, v in pairs(t) do + if o[k] == nil then o[k] = v end + end + return o +end + +function each_join(dir, list) + for i, v in ipairs(list) do + list[i] = path.join(dir, v) + end + return list +end + +function run(file, cwd) + print() + print("run " .. file) + if not TESTING then + if cwd then lake.chdir(cwd) end + local status, code = utils.execute( LUA_RUNNER .. ' ' .. file ) + if cwd then lake.chdir("<") end + print() + return status, code + end + return true, 0 +end + +function exec(file, cwd) + print() + print("exec " .. file) + if not TESTING then + if cwd then lake.chdir(cwd) end + local status, code = utils.execute( file ) + if cwd then lake.chdir("<") end + print() + return status, code + end + return true, 0 +end + +local TESTS = {} + +function run_test(name, params) + local test_dir = TESTDIR or J(ROOT, 'test') + local cmd = J(test_dir, name) + if params then cmd = cmd .. ' ' .. params end + local ok = run(cmd, test_dir) + + table.insert(TESTS, {cmd = cmd, result = ok}) + + print("TEST " .. name .. (ok and ' - pass!' or ' - fail!')) +end + +function exec_test(name, params) + local test_dir = TESTDIR or J(ROOT, 'test') + local cmd = J(test_dir, name) + if params then cmd = cmd .. ' ' .. params end + local ok = exec(cmd, test_dir) + + table.insert(TESTS, {cmd = cmd, result = ok}) + + print("TEST " .. name .. (ok and ' - pass!' or ' - fail!')) +end + +function test_summary() + local ok = true + print("") + print("------------------------------------") + print("Number of tests:", #TESTS) + for _, t in ipairs(TESTS) do + ok = ok and t.result + print((t.result and ' Pass' or ' Fail') .. " - TEST " .. t.cmd) + end + print("------------------------------------") + print("") + return ok +end + +--[[spawn]] if WINDOWS then + function spawn(file, cwd) + local winapi = prequire "winapi" + if not winapi then + quit('needs winapi for spawn!') + return false + end + + print("spawn " .. file) + if not TESTING then + if cwd then lake.chdir(cwd) end + assert(winapi.shell_exec(nil, LUA_RUNNER, file, cwd)) + if cwd then lake.chdir("<") end + print() + end + return true + end +else + function spawn(file, cwd) + print("spawn " .. file) + if not TESTING then + assert(run(file .. ' &', cwd)) + end + return true + end +end + +function as_bool(v,d) + if v == nil then return not not d end + local n = tonumber(v) + if n == 0 then return false end + if n then return true end + return false +end + +--- set global variables +-- LUA_NEED +-- LUA_DIR +-- LUA_RUNNER +-- ROOT +-- LUADIR +-- LIBDIR +-- TESTDIR +-- DOCDIR +-- DYNAMIC +function INITLAKEFILE() + if LUA_VER == '5.3' then + LUA_NEED = 'lua53' + LUA_DIR = ENV.LUA_DIR_5_3 or ENV.LUA_DIR + LUA_RUNNER = LUA_RUNNER or 'lua53' + elseif LUA_VER == '5.2' then + LUA_NEED = 'lua52' + LUA_DIR = ENV.LUA_DIR_5_2 or ENV.LUA_DIR + LUA_RUNNER = LUA_RUNNER or 'lua52' + elseif LUA_VER == '5.1' then + LUA_NEED = 'lua51' + LUA_DIR = ENV.LUA_DIR + LUA_RUNNER = LUA_RUNNER or 'lua' + else + LUA_NEED = 'lua' + LUA_DIR = ENV.LUA_DIR + LUA_RUNNER = LUA_RUNNER or 'lua' + end + ROOT = ROOT or J( LUA_DIR, 'libs', PROJECT ) + LUADIR = LUADIR or J( ROOT, 'share' ) + LIBDIR = LIBDIR or J( ROOT, 'share' ) + TESTDIR = TESTDIR or J( ROOT, 'test' ) + DOCDIR = DOCDIR or J( ROOT, 'doc' ) + DYNAMIC = as_bool(DYNAMIC, false) +end + +----------------------- +-- needs -- +----------------------- + +lake.define_need('lua53', function() + return { + incdir = J(ENV.LUA_DIR_5_3, 'include'); + libdir = J(ENV.LUA_DIR_5_3, 'lib'); + libs = {'lua53'}; + } +end) + +lake.define_need('lua52', function() + return { + incdir = J(ENV.LUA_DIR_5_2, 'include'); + libdir = J(ENV.LUA_DIR_5_2, 'lib'); + libs = {'lua52'}; + } +end) + +lake.define_need('lua51', function() + return { + incdir = J(ENV.LUA_DIR, 'include'); + libdir = J(ENV.LUA_DIR, 'lib'); + libs = {'lua5.1'}; + } +end) + +lake.define_need('pthread', function() + return { + libs = 'pthread'; + } +end) \ No newline at end of file diff --git a/llthreads2/lakefile b/llthreads2/lakefile new file mode 100644 index 0000000..64bd3fa --- /dev/null +++ b/llthreads2/lakefile @@ -0,0 +1,45 @@ +PROJECT = 'llthreads' + +INITLAKEFILE() + +DEFINES = L{DEFINES, + IF(WINDOWS, 'DLL_EXPORT', ''); + IF(not MSVC, 'USE_PTHREAD', ''); +} + +core = c.shared{PROJECT, + base = 'src', + src = '*.c', + needs = LUA_NEED, + defines = DEFINES, + dynamic = DYNAMIC, + strip = true, + libs = IF(not MSVC, 'pthread'); +} + +target('build', core) + +install = target('install', { + file.group{odir=LIBDIR; src = core }; + file.group{odir=TESTDIR; src = J('test', '*'); recurse = true }; +}) + +target('test', install, function() + -- run_test('test_register_llthreads.lua') + run_test('test_join_timeout.lua') + run_test('test_llthreads.lua') + run_test('test_table_copy.lua') + run_test('test_threads.lua') + run_test('test_join_timeout.lua') + run_test('test_join_detach.lua') + run_test('test_join_error.lua') + run_test('test_register_ffi.lua') + run_test('test_logger.lua') + run_test('test_pass_cfunction.lua') + run_test('test_alive.lua') + + if not test_summary() then + quit("test fail") + end +end) + diff --git a/llthreads2/msvc/llthreads.sln b/llthreads2/msvc/llthreads.sln new file mode 100644 index 0000000..1277174 --- /dev/null +++ b/llthreads2/msvc/llthreads.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "llthreads", "llthreads.vcproj", "{60F3B657-C1F7-47F7-B159-3EEEA6B1220D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {60F3B657-C1F7-47F7-B159-3EEEA6B1220D}.Debug|Win32.ActiveCfg = Debug|Win32 + {60F3B657-C1F7-47F7-B159-3EEEA6B1220D}.Debug|Win32.Build.0 = Debug|Win32 + {60F3B657-C1F7-47F7-B159-3EEEA6B1220D}.Release|Win32.ActiveCfg = Release|Win32 + {60F3B657-C1F7-47F7-B159-3EEEA6B1220D}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/llthreads2/msvc/llthreads.vcproj b/llthreads2/msvc/llthreads.vcproj new file mode 100644 index 0000000..6f6c5bd --- /dev/null +++ b/llthreads2/msvc/llthreads.vcproj @@ -0,0 +1,208 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/llthreads2/rockspecs/lua-llthreads2-0.1.0-1.rockspec b/llthreads2/rockspecs/lua-llthreads2-0.1.0-1.rockspec new file mode 100644 index 0000000..9116d6a --- /dev/null +++ b/llthreads2/rockspecs/lua-llthreads2-0.1.0-1.rockspec @@ -0,0 +1,44 @@ +package = "lua-llthreads2" +version = "0.1.0-1" +source = { + url = "https://github.com/moteus/lua-llthreads2/archive/v0.1.0.zip", + dir = "lua-llthreads2-0.1.0", +} +description = { + summary = "Low-Level threads for Lua", + homepage = "http://github.com/moteus/lua-llthreads2", + license = "MIT/X11", + detailed = [[ + This is drop-in replacement for `lua-llthread` module but the module called `llthreads2`. + In additional module supports: thread join with zero timeout; logging thread errors with + custom logger; run detached joinable threads; pass cfunctions as argument to child thread. + ]], +} +dependencies = { + "lua >= 5.1, < 5.3", +} +build = { + type = "builtin", + platforms = { + unix = { + modules = { + llthreads2 = { + libraries = {"pthread"}, + } + } + }, + windows = { + modules = { + llthreads2 = { + libraries = {"kernel32"}, + } + } + } + }, + modules = { + llthreads2 = { + sources = { "src/l52util.c", "src/llthread.c" }, + defines = { "LLTHREAD_MODULE_NAME=llthreads2" }, + } + } +} \ No newline at end of file diff --git a/llthreads2/rockspecs/lua-llthreads2-0.1.1-1.rockspec b/llthreads2/rockspecs/lua-llthreads2-0.1.1-1.rockspec new file mode 100644 index 0000000..af74686 --- /dev/null +++ b/llthreads2/rockspecs/lua-llthreads2-0.1.1-1.rockspec @@ -0,0 +1,44 @@ +package = "lua-llthreads2" +version = "0.1.1-1" +source = { + url = "https://github.com/moteus/lua-llthreads2/archive/v0.1.1.zip", + dir = "lua-llthreads2-0.1.1", +} +description = { + summary = "Low-Level threads for Lua", + homepage = "http://github.com/moteus/lua-llthreads2", + license = "MIT/X11", + detailed = [[ + This is drop-in replacement for `lua-llthread` module but the module called `llthreads2`. + In additional module supports: thread join with zero timeout; logging thread errors with + custom logger; run detached joinable threads; pass cfunctions as argument to child thread. + ]], +} +dependencies = { + "lua >= 5.1, < 5.3", +} +build = { + type = "builtin", + platforms = { + unix = { + modules = { + llthreads2 = { + libraries = {"pthread"}, + } + } + }, + windows = { + modules = { + llthreads2 = { + libraries = {"kernel32"}, + } + } + } + }, + modules = { + llthreads2 = { + sources = { "src/l52util.c", "src/llthread.c" }, + defines = { "LLTHREAD_MODULE_NAME=llthreads2" }, + } + } +} \ No newline at end of file diff --git a/llthreads2/rockspecs/lua-llthreads2-compat-0.1.0-1.rockspec b/llthreads2/rockspecs/lua-llthreads2-compat-0.1.0-1.rockspec new file mode 100644 index 0000000..0c058ea --- /dev/null +++ b/llthreads2/rockspecs/lua-llthreads2-compat-0.1.0-1.rockspec @@ -0,0 +1,44 @@ +package = "lua-llthreads2-compat" +version = "0.1.0-1" +source = { + url = "https://github.com/moteus/lua-llthreads2/archive/v0.1.0.zip", + dir = "lua-llthreads2-0.1.0", +} +description = { + summary = "Low-Level threads for Lua", + homepage = "http://github.com/moteus/lua-llthreads2", + license = "MIT/X11", + detailed = [[ + This is drop-in replacement for `lua-llthread` module. + In additional module supports: thread join with zero timeout; logging thread errors with + custom logger; run detached joinable threads; pass cfunctions as argument to child thread. + ]], +} +dependencies = { + "lua >= 5.1, < 5.3", +} +build = { + type = "builtin", + platforms = { + unix = { + modules = { + llthreads = { + libraries = {"pthread"}, + } + } + }, + windows = { + modules = { + llthreads = { + libraries = {"kernel32"}, + } + } + } + }, + modules = { + llthreads = { + sources = { "src/l52util.c", "src/llthread.c" }, + defines = { "LLTHREAD_MODULE_NAME=llthreads" }, + } + } +} \ No newline at end of file diff --git a/llthreads2/rockspecs/lua-llthreads2-compat-0.1.1-1.rockspec b/llthreads2/rockspecs/lua-llthreads2-compat-0.1.1-1.rockspec new file mode 100644 index 0000000..75ef40e --- /dev/null +++ b/llthreads2/rockspecs/lua-llthreads2-compat-0.1.1-1.rockspec @@ -0,0 +1,44 @@ +package = "lua-llthreads2-compat" +version = "0.1.1-1" +source = { + url = "https://github.com/moteus/lua-llthreads2/archive/v0.1.1.zip", + dir = "lua-llthreads2-0.1.1", +} +description = { + summary = "Low-Level threads for Lua", + homepage = "http://github.com/moteus/lua-llthreads2", + license = "MIT/X11", + detailed = [[ + This is drop-in replacement for `lua-llthread` module. + In additional module supports: thread join with zero timeout; logging thread errors with + custom logger; run detached joinable threads; pass cfunctions as argument to child thread. + ]], +} +dependencies = { + "lua >= 5.1, < 5.3", +} +build = { + type = "builtin", + platforms = { + unix = { + modules = { + llthreads = { + libraries = {"pthread"}, + } + } + }, + windows = { + modules = { + llthreads = { + libraries = {"kernel32"}, + } + } + } + }, + modules = { + llthreads = { + sources = { "src/l52util.c", "src/llthread.c" }, + defines = { "LLTHREAD_MODULE_NAME=llthreads" }, + } + } +} \ No newline at end of file diff --git a/llthreads2/rockspecs/lua-llthreads2-compat-scm-0.rockspec b/llthreads2/rockspecs/lua-llthreads2-compat-scm-0.rockspec new file mode 100644 index 0000000..ac81a15 --- /dev/null +++ b/llthreads2/rockspecs/lua-llthreads2-compat-scm-0.rockspec @@ -0,0 +1,44 @@ +package = "lua-llthreads2-compat" +version = "scm-0" +source = { + url = "https://github.com/moteus/lua-llthreads2/archive/master.zip", + dir = "lua-llthreads2-master", +} +description = { + summary = "Low-Level threads for Lua", + homepage = "http://github.com/moteus/lua-llthreads2", + license = "MIT/X11", + detailed = [[ + This is drop-in replacement for `lua-llthread` module. + In additional module supports: thread join with zero timeout; logging thread errors with + custom logger; run detached joinable threads; pass cfunctions as argument to child thread. + ]], +} +dependencies = { + "lua >= 5.1, < 5.3", +} +build = { + type = "builtin", + platforms = { + unix = { + modules = { + llthreads = { + libraries = {"pthread"}, + } + } + }, + windows = { + modules = { + llthreads = { + libraries = {"kernel32"}, + } + } + } + }, + modules = { + llthreads = { + sources = { "src/l52util.c", "src/llthread.c" }, + defines = { "LLTHREAD_MODULE_NAME=llthreads" }, + } + } +} \ No newline at end of file diff --git a/llthreads2/rockspecs/lua-llthreads2-scm-0.rockspec b/llthreads2/rockspecs/lua-llthreads2-scm-0.rockspec new file mode 100644 index 0000000..87f9b15 --- /dev/null +++ b/llthreads2/rockspecs/lua-llthreads2-scm-0.rockspec @@ -0,0 +1,44 @@ +package = "lua-llthreads2" +version = "scm-0" +source = { + url = "https://github.com/moteus/lua-llthreads2/archive/master.zip", + dir = "lua-llthreads2-master", +} +description = { + summary = "Low-Level threads for Lua", + homepage = "http://github.com/moteus/lua-llthreads2", + license = "MIT/X11", + detailed = [[ + This is drop-in replacement for `lua-llthread` module but the module called `llthreads2`. + In additional module supports: thread join with zero timeout; logging thread errors with + custom logger; run detached joinable threads; pass cfunctions as argument to child thread. + ]], +} +dependencies = { + "lua >= 5.1, < 5.3", +} +build = { + type = "builtin", + platforms = { + unix = { + modules = { + llthreads2 = { + libraries = {"pthread"}, + } + } + }, + windows = { + modules = { + llthreads2 = { + libraries = {"kernel32"}, + } + } + } + }, + modules = { + llthreads2 = { + sources = { "src/l52util.c", "src/llthread.c" }, + defines = { "LLTHREAD_MODULE_NAME=llthreads2" }, + } + } +} \ No newline at end of file diff --git a/llthreads2/src/copy.inc b/llthreads2/src/copy.inc new file mode 100644 index 0000000..21ba261 --- /dev/null +++ b/llthreads2/src/copy.inc @@ -0,0 +1,170 @@ +/****************************************************************************** +* Copyright (c) 2011 by Robert G. Jakabosky +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ + +/* maximum recursive depth of table copies. */ +#define MAX_COPY_DEPTH 30 + +typedef struct { + lua_State *from_L; + lua_State *to_L; + int has_cache; + int cache_idx; + int is_arg; +} llthread_copy_state; + +static int llthread_copy_table_from_cache(llthread_copy_state *state, int idx) { + void *ptr; + + /* convert table to pointer for lookup in cache. */ + ptr = (void *)lua_topointer(state->from_L, idx); + if(ptr == NULL) return 0; /* can't convert to pointer. */ + + /* check if we need to create the cache. */ + if(!state->has_cache) { + lua_newtable(state->to_L); + lua_replace(state->to_L, state->cache_idx); + state->has_cache = 1; + } + + lua_pushlightuserdata(state->to_L, ptr); + lua_rawget(state->to_L, state->cache_idx); + if(lua_isnil(state->to_L, -1)) { + /* not in cache. */ + lua_pop(state->to_L, 1); + /* create new table and add to cache. */ + lua_newtable(state->to_L); + lua_pushlightuserdata(state->to_L, ptr); + lua_pushvalue(state->to_L, -2); + lua_rawset(state->to_L, state->cache_idx); + return 0; + } + /* found table in cache. */ + return 1; +} + +static int llthread_copy_value(llthread_copy_state *state, int depth, int idx) { + const char *str; + size_t str_len; + int kv_pos; + + /* Maximum recursive depth */ + if(++depth > MAX_COPY_DEPTH) { + return luaL_error(state->from_L, "Hit maximum copy depth (%d > %d).", depth, MAX_COPY_DEPTH); + } + + /* only support string/number/boolean/nil/table/lightuserdata. */ + switch(lua_type(state->from_L, idx)) { + case LUA_TNIL: + lua_pushnil(state->to_L); + break; + case LUA_TNUMBER: + lua_pushnumber(state->to_L, lua_tonumber(state->from_L, idx)); + break; + case LUA_TBOOLEAN: + lua_pushboolean(state->to_L, lua_toboolean(state->from_L, idx)); + break; + case LUA_TSTRING: + str = lua_tolstring(state->from_L, idx, &(str_len)); + lua_pushlstring(state->to_L, str, str_len); + break; + case LUA_TLIGHTUSERDATA: + lua_pushlightuserdata(state->to_L, lua_touserdata(state->from_L, idx)); + break; + case LUA_TTABLE: + /* make sure there is room on the new state for 3 values (table,key,value) */ + if(!lua_checkstack(state->to_L, 3)) { + return luaL_error(state->from_L, "To stack overflow!"); + } + /* make room on from stack for key/value pairs. */ + luaL_checkstack(state->from_L, 2, "From stack overflow!"); + + /* check cache for table. */ + if(llthread_copy_table_from_cache(state, idx)) { + /* found in cache don't need to copy table. */ + break; + } + lua_pushnil(state->from_L); + while (lua_next(state->from_L, idx) != 0) { + /* key is at (top - 1), value at (top), but we need to normalize these + * to positive indices */ + kv_pos = lua_gettop(state->from_L); + /* copy key */ + llthread_copy_value(state, depth, kv_pos - 1); + /* copy value */ + llthread_copy_value(state, depth, kv_pos); + /* Copied key and value are now at -2 and -1 in state->to_L. */ + lua_settable(state->to_L, -3); + /* Pop value for next iteration */ + lua_pop(state->from_L, 1); + } + break; + case LUA_TFUNCTION: + if(lua_iscfunction(state->from_L, idx)){ + lua_CFunction fn = lua_tocfunction(state->from_L, idx); + lua_pushcfunction(state->to_L, fn); + break; + } + case LUA_TUSERDATA: + case LUA_TTHREAD: + default: + if (state->is_arg) { + return luaL_argerror(state->from_L, idx, "function/userdata/thread types un-supported."); + } else { + /* convert un-supported types to an error string. */ + lua_pushfstring(state->to_L, "Un-supported value: %s: %p", + lua_typename(state->from_L, lua_type(state->from_L, idx)), lua_topointer(state->from_L, idx)); + } + } + + return 1; +} + +static int llthread_copy_values(lua_State *from_L, lua_State *to_L, int idx, int top, int is_arg) { + llthread_copy_state state; + int nvalues = 0; + int n; + + nvalues = (top - idx) + 1; + /* make sure there is room on the new state for the values. */ + if(!lua_checkstack(to_L, nvalues + 1)) { + return luaL_error(from_L, "To stack overflow!"); + } + + /* setup copy state. */ + state.from_L = from_L; + state.to_L = to_L; + state.is_arg = is_arg; + state.has_cache = 0; /* don't create cache table unless it is needed. */ + lua_pushnil(to_L); + state.cache_idx = lua_gettop(to_L); + + nvalues = 0; + for(n = idx; n <= top; n++) { + llthread_copy_value(&state, 0, n); + ++nvalues; + } + + /* remove cache table. */ + lua_remove(to_L, state.cache_idx); + + return nvalues; +} diff --git a/llthreads2/src/l52util.c b/llthreads2/src/l52util.c new file mode 100644 index 0000000..9d44a40 --- /dev/null +++ b/llthreads2/src/l52util.c @@ -0,0 +1,126 @@ +#include "l52util.h" + +#include +#include + +#if LUA_VERSION_NUM >= 502 + +int luaL_typerror (lua_State *L, int narg, const char *tname) { + const char *msg = lua_pushfstring(L, "%s expected, got %s", tname, + luaL_typename(L, narg)); + return luaL_argerror(L, narg, msg); +} + +#ifndef luaL_register + +void luaL_register (lua_State *L, const char *libname, const luaL_Reg *l){ + if(libname) lua_newtable(L); + luaL_setfuncs(L, l, 0); +} + +#endif + +#else + +void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup){ + luaL_checkstack(L, nup, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ + int i; + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -nup); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + lua_setfield(L, -(nup + 2), l->name); + } + lua_pop(L, nup); /* remove upvalues */ +} + +void lua_rawgetp(lua_State *L, int index, const void *p){ + index = lua_absindex(L, index); + lua_pushlightuserdata(L, (void *)p); + lua_rawget(L, index); +} + +void lua_rawsetp (lua_State *L, int index, const void *p){ + index = lua_absindex(L, index); + lua_pushlightuserdata(L, (void *)p); + lua_insert(L, -2); + lua_rawset(L, index); +} + +void lutil_require(lua_State *L, const char* name, lua_CFunction fn, int glb) { + // @fixme generate error if we can not load module + lua_cpcall(L, fn, NULL); +} + +#endif + +int lutil_newmetatablep (lua_State *L, const void *p) { + lua_rawgetp(L, LUA_REGISTRYINDEX, p); + if (!lua_isnil(L, -1)) /* name already in use? */ + return 0; /* leave previous value on top, but return 0 */ + lua_pop(L, 1); + + lua_newtable(L); /* create metatable */ + lua_pushvalue(L, -1); /* duplicate metatable to set*/ + lua_rawsetp(L, LUA_REGISTRYINDEX, p); + + return 1; +} + +void lutil_getmetatablep (lua_State *L, const void *p) { + lua_rawgetp(L, LUA_REGISTRYINDEX, p); +} + +void lutil_setmetatablep (lua_State *L, const void *p) { + lutil_getmetatablep(L, p); + assert(lua_istable(L,-1)); + lua_setmetatable (L, -2); +} + +int lutil_isudatap (lua_State *L, int ud, const void *p) { + if (lua_isuserdata(L, ud)){ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + int res; + lutil_getmetatablep(L,p); /* get correct metatable */ + res = lua_rawequal(L, -1, -2); /* does it have the correct mt? */ + lua_pop(L, 2); /* remove both metatables */ + return res; + } + } + return 0; +} + +void *lutil_checkudatap (lua_State *L, int ud, const void *p) { + void *up = lua_touserdata(L, ud); + if (up != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + lutil_getmetatablep(L,p); /* get correct metatable */ + if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */ + lua_pop(L, 2); /* remove both metatables */ + return up; + } + } + } + luaL_typerror(L, ud, p); /* else error */ + return NULL; /* to avoid warnings */ +} + +int lutil_createmetap (lua_State *L, const void *p, const luaL_Reg *methods, int nup) { + if (!lutil_newmetatablep(L, p)) + return 0; + + lua_insert(L, -1 - nup); /* move mt prior upvalues */ + luaL_setfuncs (L, methods, nup); /* define methods */ + lua_pushliteral (L, "__index"); /* define metamethods */ + lua_pushvalue (L, -2); + lua_settable (L, -3); + + return 1; +} + +void *lutil_newudatap_impl(lua_State *L, size_t size, const void *p){ + void *obj = lua_newuserdata (L, size); + memset(obj, 0, size); + lutil_setmetatablep(L, p); + return obj; +} diff --git a/llthreads2/src/l52util.h b/llthreads2/src/l52util.h new file mode 100644 index 0000000..38a87db --- /dev/null +++ b/llthreads2/src/l52util.h @@ -0,0 +1,57 @@ +#ifndef _LZUTILS_H_9B43D914_9652_4E22_9A43_8073502BF3F4_ +#define _LZUTILS_H_9B43D914_9652_4E22_9A43_8073502BF3F4_ + +#include "lua.h" +#include "lauxlib.h" + +#if LUA_VERSION_NUM >= 502 // lua 5.2 + +// lua_rawgetp +// lua_rawsetp +// luaL_setfuncs +// lua_absindex + +#ifndef lua_objlen + +#define lua_objlen lua_rawlen + +#endif + +int luaL_typerror (lua_State *L, int narg, const char *tname); + +#ifndef luaL_register + +void luaL_register (lua_State *L, const char *libname, const luaL_Reg *l); + +#endif + +#define lutil_require luaL_requiref + +#else // lua 5.1 + +// functions form lua 5.2 + +# define lua_absindex(L, i) (((i)>0)?(i):((i)<=LUA_REGISTRYINDEX?(i):(lua_gettop(L)+(i)+1))) +# define lua_rawlen lua_objlen + +void lua_rawgetp (lua_State *L, int index, const void *p); +void lua_rawsetp (lua_State *L, int index, const void *p); +void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup); + +void lutil_require(lua_State *L, const char* name, lua_CFunction fn, int glb); + +#endif + +int lutil_newmetatablep (lua_State *L, const void *p); +void lutil_getmetatablep (lua_State *L, const void *p); +void lutil_setmetatablep (lua_State *L, const void *p); + +#define lutil_newudatap(L, TTYPE, TNAME) (TTYPE *)lutil_newudatap_impl(L, sizeof(TTYPE), TNAME) +int lutil_isudatap (lua_State *L, int ud, const void *p); +void *lutil_checkudatap (lua_State *L, int ud, const void *p); +int lutil_createmetap (lua_State *L, const void *p, const luaL_Reg *methods, int nup); + +void *lutil_newudatap_impl (lua_State *L, size_t size, const void *p); + +#endif + diff --git a/llthreads2/src/llthread.c b/llthreads2/src/llthread.c new file mode 100644 index 0000000..0ffa727 --- /dev/null +++ b/llthreads2/src/llthread.c @@ -0,0 +1,746 @@ +#if !defined(_WIN32) && !defined(USE_PTHREAD) +# define USE_PTHREAD +#endif + +#define LLTHREAD_VERSION_MAJOR 0 +#define LLTHREAD_VERSION_MINOR 1 +#define LLTHREAD_VERSION_PATCH 0 +#define LLTHREAD_VERSION_COMMENT "" + +#ifndef USE_PTHREAD +# include +# include +#else +# include +#endif + +#include +#include +#include +#include +#include +#include +#include "l52util.h" +#include "traceback.inc" +#include "copy.inc" + +/*export*/ +#ifdef _WIN32 +# define LLTHREADS_EXPORT_API __declspec(dllexport) +#else +# define LLTHREADS_EXPORT_API LUALIB_API +#endif + +/* wrap strerror_s(). */ +#ifdef _WIN32 +# ifdef __GNUC__ +# ifndef strerror_r +# define strerror_r(errno, buf, buflen) do { \ + strncpy((buf), strerror(errno), (buflen)-1); \ + (buf)[(buflen)-1] = '\0'; \ + } while(0) +# endif +# else +# ifndef strerror_r +# define strerror_r(errno, buf, buflen) strerror_s((buf), (buflen), (errno)) +# endif +# endif +#endif + +#ifndef USE_PTHREAD +# define OS_THREAD_RETURN unsigned int __stdcall +# define INVALID_THREAD INVALID_HANDLE_VALUE +# define INFINITE_JOIN_TIMEOUT INFINITE +# define JOIN_OK 0 +# define JOIN_ETIMEDOUT 1 +# define JOIN_FAIL 2 +typedef DWORD join_timeout_t; +typedef HANDLE os_thread_t; +#else +# define OS_THREAD_RETURN void * +# define INFINITE_JOIN_TIMEOUT -1 +# define JOIN_OK 0 +# define JOIN_ETIMEDOUT ETIMEDOUT +typedef int join_timeout_t; +typedef pthread_t os_thread_t; +#endif + +#define ERROR_LEN 1024 + +#define flags_t unsigned char + +#define FLAG_NONE (flags_t)0 +#define FLAG_STARTED (flags_t)1<<0 +#define FLAG_DETACHED (flags_t)1<<1 +#define FLAG_JOINED (flags_t)1<<2 +#define FLAG_JOINABLE (flags_t)1<<3 + +/*At least one flag*/ +#define FLAG_IS_SET(O, F) (O->flags & (flags_t)(F)) +#define FLAG_SET(O, F) O->flags |= (flags_t)(F) +#define FLAG_UNSET(O, F) O->flags &= ~((flags_t)(F)) +#define IS(O, F) FLAG_IS_SET(O, FLAG_##F) +#define SET(O, F) FLAG_SET(O, FLAG_##F) + +#define ALLOC_STRUCT(S) (S*)calloc(1, sizeof(S)) +#define FREE_STRUCT(O) free(O) + +#ifndef LLTHREAD_MODULE_NAME +# define LLTHREAD_MODULE_NAME llthreads +#endif + +#define CAT(S1,S2) S1##S2 + +#define LLTHREAD_OPEN_NAME_IMPL(NAME) CAT(luaopen_, NAME) + +#define LLTHREAD_OPEN_NAME LLTHREAD_OPEN_NAME_IMPL(LLTHREAD_MODULE_NAME) + +LLTHREADS_EXPORT_API int LLTHREAD_OPEN_NAME(lua_State *L); + +#define LLTHREAD_NAME "LLThread" +static const char *LLTHREAD_TAG = LLTHREAD_NAME; +static const char *LLTHREAD_LOGGER_HOLDER = LLTHREAD_NAME " logger holder"; + +typedef struct llthread_child_t { + lua_State *L; + int status; + flags_t flags; +} llthread_child_t; + +typedef struct llthread_t { + llthread_child_t *child; + os_thread_t thread; + flags_t flags; +} llthread_t; + +static int fail(lua_State *L, const char *msg){ + lua_pushnil(L); + lua_pushstring(L, msg); + return 2; +} + +//{ logger interface +void llthread_log(lua_State *L, const char *hdr, const char *msg){ + int top = lua_gettop(L); + lua_rawgetp(L, LUA_REGISTRYINDEX, LLTHREAD_LOGGER_HOLDER); + if(lua_isnil(L, -1)){ + lua_pop(L, 1); + fputs(hdr, stderr); + fputs(msg, stderr); + fputc('\n', stderr); + fflush(stderr); + return; + } + lua_pushstring(L, hdr); + lua_pushstring(L, msg); + lua_concat(L, 2); + lua_pcall(L, 1, 0, 0); + lua_settop(L, top); +} +//} + +//{ llthread_child + +static void open_thread_libs(lua_State *L){ +#define L_REGLIB(L, name) lua_pushcfunction(L, luaopen_##name); lua_setfield(L, -2) + + int top = lua_gettop(L); + +#ifndef LLTHREAD_REGISTER_STD_LIBRARY + + luaL_openlibs(L); + lua_getglobal(L, "package"); lua_getfield(L, -1, "preload"); lua_remove(L, -2); + +#else + + lutil_require(L, "_G", luaopen_base, 1); + lutil_require(L, "package", luaopen_package, 1); + lua_settop(L, top); + + /* get package.preload */ + lua_getglobal(L, "package"); lua_getfield(L, -1, "preload"); lua_remove(L, -2); + L_REGLIB(L, io, 1); + L_REGLIB(L, os, 1); + L_REGLIB(L, math, 1); + L_REGLIB(L, table, 1); + L_REGLIB(L, string, 1); + +#ifdef LUA_DBLIBNAME + L_REGLIB(L, debug, 1); +#endif + + /* @fixme find out luaopen_XXX at runtime */ +#ifdef LUA_JITLIBNAME + L_REGLIB(L, bit, 1); + L_REGLIB(L, jit, 1); + L_REGLIB(L, ffi, 1); +#elif defined LUA_BITLIBNAME + L_REGLIB(L, bit32, 1); +#endif + +#endif + +#ifdef LLTHREAD_REGISTER_THREAD_LIBRARY + L_REGLIB(L, llthreads, 0); +#endif + + lua_settop(L, top); + +#undef L_REGLIB +} + +static llthread_child_t *llthread_child_new() { + llthread_child_t *this = ALLOC_STRUCT(llthread_child_t); + if(!this) return NULL; + + memset(this, 0, sizeof(llthread_child_t)); + + /* create new lua_State for the thread. */ + /* open standard libraries. */ + this->L = luaL_newstate(); + open_thread_libs(this->L); + + return this; +} + +static void llthread_child_destroy(llthread_child_t *this) { + lua_close(this->L); + FREE_STRUCT(this); +} + +static OS_THREAD_RETURN llthread_child_thread_run(void *arg) { + llthread_child_t *this = (llthread_child_t *)arg; + lua_State *L = this->L; + int nargs = lua_gettop(L) - 1; + + /* push traceback function as first value on stack. */ + lua_pushcfunction(this->L, traceback); + lua_insert(L, 1); + + this->status = lua_pcall(L, nargs, LUA_MULTRET, 1); + + /* alwasy print errors here, helps with debugging bad code. */ + if(this->status != 0) { + llthread_log(L, "Error from thread: ", lua_tostring(L, -1)); + } + + if(IS(this, DETACHED) || !IS(this, JOINABLE)) { + /* thread is detached, so it must clean-up the child state. */ + llthread_child_destroy(this); + this = NULL; + } + +#ifndef USE_PTHREAD + if(this) { + /* attached thread, don't close thread handle. */ + _endthreadex(0); + } else { + /* detached thread, close thread handle. */ + _endthread(); + } + return 0; +#else + return this; +#endif +} + +//} + +//{ llthread + +static void llthread_validate(llthread_t *this){ + /* describe valid state of llthread_t object + * from after create and before destroy + */ + if(!IS(this, STARTED)){ + assert(!IS(this, DETACHED)); + assert(!IS(this, JOINED)); + assert(!IS(this, JOINABLE)); + return; + } + + if(IS(this, DETACHED)){ + if(!IS(this, JOINABLE)) assert(this->child == NULL); + else assert(this->child != NULL); + } +} + +static int llthread_detach(llthread_t *this); + +static int llthread_join(llthread_t *this, join_timeout_t timeout); + +static llthread_t *llthread_new() { + llthread_t *this = ALLOC_STRUCT(llthread_t); + if(!this) return NULL; + + this->flags = FLAG_NONE; +#ifndef USE_PTHREAD + this->thread = INVALID_THREAD; +#endif + this->child = llthread_child_new(); + if(!this->child){ + FREE_STRUCT(this); + return NULL; + } + + return this; +} + +static void llthread_cleanup_child(llthread_t *this) { + if(this->child) { + llthread_child_destroy(this->child); + this->child = NULL; + } +} + +static void llthread_destroy(llthread_t *this) { + do{ + /* thread not started */ + if(!IS(this, STARTED)){ + llthread_cleanup_child(this); + break; + } + + /* DETACHED */ + if(IS(this, DETACHED)){ + if(IS(this, JOINABLE)){ + llthread_detach(this); + } + break; + } + + /* ATTACHED */ + if(!IS(this, JOINED)){ + llthread_join(this, INFINITE_JOIN_TIMEOUT); + if(!IS(this, JOINED)){ + /* @todo use current lua state to logging */ + /* + * char buf[ERROR_LEN]; + * strerror_r(errno, buf, ERROR_LEN); + * llthread_log(L, "Error can not join thread on gc: ", buf); + */ + } + } + if(IS(this, JOINABLE)){ + llthread_cleanup_child(this); + } + + }while(0); + + FREE_STRUCT(this); +} + +static int llthread_push_args(lua_State *L, llthread_child_t *child, int idx, int top) { + return llthread_copy_values(L, child->L, idx, top, 1 /* is_arg */); +} + +static int llthread_push_results(lua_State *L, llthread_child_t *child, int idx, int top) { + return llthread_copy_values(child->L, L, idx, top, 0 /* is_arg */); +} + +static int llthread_detach(llthread_t *this){ + int rc = 0; + + assert(IS(this, STARTED)); + assert(this->child != NULL); + + this->child = NULL; + + /*we can not detach joined thread*/ + if(IS(this, JOINED)) + return 0; + +#ifdef USE_PTHREAD + rc = pthread_detach(this->thread); +#else + assert(this->thread != INVALID_THREAD); + CloseHandle(this->thread); + this->thread = INVALID_THREAD; +#endif + return rc; +} + +/* | detached | joinable || join | which thread | gc | detach | + * | | || return | destroy child | calls | on | + * ------------------------------------------------------------------------ + * | false | falas || | child | join | | + * *| false | true || Lua values | parent | join | | + * *| true | false || | child | | start | + * | true | true || | child | detach | gc | + * ------------------------------------------------------------------------ + * * llthread behavior. + */ +static int llthread_start(llthread_t *this, int start_detached, int joinable) { + llthread_child_t *child = this->child; + int rc = 0; + + llthread_validate(this); + + if(joinable) SET(child, JOINABLE); + if(start_detached) SET(child, DETACHED); + +#ifndef USE_PTHREAD + this->thread = (HANDLE)_beginthreadex(NULL, 0, llthread_child_thread_run, child, 0, NULL); + if(INVALID_THREAD == this->thread){ + rc = -1; + } +#else + rc = pthread_create(&(this->thread), NULL, llthread_child_thread_run, child); +#endif + + if(rc == 0) { + SET(this, STARTED); + if(joinable) SET(this, JOINABLE); + if(start_detached) SET(this, DETACHED); + if((start_detached)&&(!joinable)){ + rc = llthread_detach(this); + } + } + + llthread_validate(this); + + return rc; +} + +static int llthread_join(llthread_t *this, join_timeout_t timeout) { + llthread_validate(this); + + if(IS(this, JOINED)){ + return JOIN_OK; + } else{ +#ifndef USE_PTHREAD + DWORD ret = 0; + if(INVALID_THREAD == this->thread) return JOIN_OK; + ret = WaitForSingleObject( this->thread, timeout ); + if( ret == WAIT_OBJECT_0){ /* Destroy the thread object. */ + CloseHandle( this->thread ); + this->thread = INVALID_THREAD; + SET(this, JOINED); + + llthread_validate(this); + + return JOIN_OK; + } + else if( ret == WAIT_TIMEOUT ){ + return JOIN_ETIMEDOUT; + } + return JOIN_FAIL; +#else + int rc; + if(timeout == 0){ + rc = pthread_kill(this->thread, 0); + if(rc == 0){ /* still alive */ + return JOIN_ETIMEDOUT; + } + + if(rc != ESRCH){ + /*@fixme what else it can be ?*/ + return rc; + } + + /*thread dead so we call join to free pthread_t struct */ + } + + /* @todo use pthread_tryjoin_np/pthread_timedjoin_np to support timeout */ + + /* then join the thread. */ + rc = pthread_join(this->thread, NULL); + if((rc == 0) || (rc == ESRCH)) { + SET(this, JOINED); + rc = JOIN_OK; + } + + llthread_validate(this); + + return rc; +#endif + } +} + +static int llthread_alive(llthread_t *this) { + llthread_validate(this); + + if(IS(this, JOINED)){ + return JOIN_OK; + } else{ +#ifndef USE_PTHREAD + DWORD ret = 0; + if(INVALID_THREAD == this->thread) return JOIN_OK; + ret = WaitForSingleObject( this->thread, 0 ); + if( ret == WAIT_OBJECT_0) return JOIN_OK; + if( ret == WAIT_TIMEOUT ) return JOIN_ETIMEDOUT; + return JOIN_FAIL; +#else + int rc = pthread_kill(this->thread, 0); + if(rc == 0){ /* still alive */ + return JOIN_ETIMEDOUT; + } + + if(rc != ESRCH){ + /*@fixme what else it can be ?*/ + return rc; + } + + return JOIN_OK; +#endif + } +} + +static llthread_t *llthread_create(lua_State *L, const char *code, size_t code_len) { + llthread_t *this = llthread_new(); + llthread_child_t *child = this->child; + + /* load Lua code into child state. */ + int rc = luaL_loadbuffer(child->L, code, code_len, code); + if(rc != 0) { + /* copy error message to parent state. */ + size_t len; const char *str = lua_tolstring(child->L, -1, &len); + if(str != NULL) { + lua_pushlstring(L, str, len); + } else { + /* non-string error message. */ + lua_pushfstring(L, "luaL_loadbuffer() failed to load Lua code: rc=%d", rc); + } + llthread_destroy(this); + lua_error(L); + return NULL; + } + + /* copy extra args from main state to child state. */ + /* Push all args after the Lua code. */ + llthread_push_args(L, child, 3, lua_gettop(L)); + + llthread_validate(this); + + return this; +} + +//} + +//{ Lua interface to llthread + +static llthread_t *l_llthread_at (lua_State *L, int i) { + llthread_t **this = (llthread_t **)lutil_checkudatap (L, i, LLTHREAD_TAG); + luaL_argcheck (L, this != NULL, i, "thread expected"); + luaL_argcheck (L, *this != NULL, i, "thread expected"); + // luaL_argcheck (L, !(counter->flags & FLAG_DESTROYED), 1, "PDH Counter is destroyed"); + return *this; +} + +static int l_llthread_delete(lua_State *L) { + llthread_t **pthis = (llthread_t **)lutil_checkudatap (L, 1, LLTHREAD_TAG); + luaL_argcheck (L, pthis != NULL, 1, "thread expected"); + if(*pthis == NULL) return 0; + llthread_destroy(*pthis); + *pthis = NULL; + + return 0; +} + +static int l_llthread_start(lua_State *L) { + llthread_t *this = l_llthread_at(L, 1); + int start_detached = lua_toboolean(L, 2); + int joinable, rc; + + if(!lua_isnone(L, 3)) joinable = lua_toboolean(L, 3); + else joinable = start_detached ? 0 : 1; + + if(IS(this, STARTED)) { + return fail(L, "Thread already started."); + } + + rc = llthread_start(this, start_detached, joinable); + if(rc != 0) { + char buf[ERROR_LEN]; + strerror_r(errno, buf, ERROR_LEN); + return fail(L, buf); + } + + lua_settop(L, 1); // return this + return 1; +} + +static int l_llthread_join(lua_State *L) { + llthread_t *this = l_llthread_at(L, 1); + llthread_child_t *child = this->child; + int rc; + + if(!IS(this, STARTED )) { + return fail(L, "Can't join a thread that hasn't be started."); + } + if( IS(this, DETACHED) && !IS(this, JOINABLE)) { + return fail(L, "Can't join a thread that has been detached."); + } + if( IS(this, JOINED )) { + return fail(L, "Can't join a thread that has already been joined."); + } + + /* join the thread. */ + rc = llthread_join(this, luaL_optint(L, 2, INFINITE_JOIN_TIMEOUT)); + + if(child && IS(this, JOINED)) { + int top; + + if(IS(this, DETACHED) || !IS(this, JOINABLE)){ + /*child lua state has been destroyed by child thread*/ + /*@todo return thread exit code*/ + lua_pushboolean(L, 1); + lua_pushnumber(L, 0); + return 2; + } + + /* copy values from child lua state */ + if(child->status != 0) { + const char *err_msg = lua_tostring(child->L, -1); + lua_pushboolean(L, 0); + lua_pushfstring(L, "Error from child thread: %s", err_msg); + top = 2; + } else { + lua_pushboolean(L, 1); + top = lua_gettop(child->L); + /* return results to parent thread. */ + llthread_push_results(L, child, 2, top); + } + + llthread_cleanup_child(this); + return top; + } + + if( rc == JOIN_ETIMEDOUT ){ + return fail(L, "timeout"); + } + + { + char buf[ERROR_LEN]; + strerror_r(errno, buf, ERROR_LEN); + + /* llthread_cleanup_child(this); */ + + return fail(L, buf); + } + +} + +static int l_llthread_alive(lua_State *L) { + llthread_t *this = l_llthread_at(L, 1); + llthread_child_t *child = this->child; + int rc; + + if(!IS(this, STARTED )) { + return fail(L, "Can't join a thread that hasn't be started."); + } + if( IS(this, DETACHED) && !IS(this, JOINABLE)) { + return fail(L, "Can't join a thread that has been detached."); + } + if( IS(this, JOINED )) { + return fail(L, "Can't join a thread that has already been joined."); + } + + /* join the thread. */ + rc = llthread_alive(this); + + if( rc == JOIN_ETIMEDOUT ){ + lua_pushboolean(L, 1); + return 1; + } + + if(rc == JOIN_OK){ + lua_pushboolean(L, 0); + return 1; + } + + { + char buf[ERROR_LEN]; + strerror_r(errno, buf, ERROR_LEN); + + /* llthread_cleanup_child(this); */ + + return fail(L, buf); + } + +} + +static int l_llthread_new(lua_State *L) { + size_t lua_code_len; const char *lua_code = luaL_checklstring(L, 1, &lua_code_len); + llthread_t **this = lutil_newudatap(L, llthread_t*, LLTHREAD_TAG); + lua_insert(L, 2); /*move self prior args*/ + *this = llthread_create(L, lua_code, lua_code_len); + + lua_settop(L, 2); + return 1; +} + +static const struct luaL_Reg l_llthread_meth[] = { + {"start", l_llthread_start }, + {"join", l_llthread_join }, + {"alive", l_llthread_alive }, + {"__gc", l_llthread_delete }, + + {NULL, NULL} +}; + +//} + +//{ version + +static int l_llthread_version(lua_State *L){ + lua_pushnumber(L, LLTHREAD_VERSION_MAJOR); + lua_pushnumber(L, LLTHREAD_VERSION_MINOR); + lua_pushnumber(L, LLTHREAD_VERSION_PATCH); +#ifdef LLTHREAD_VERSION_COMMENT + if(LLTHREAD_VERSION_COMMENT[0]){ + lua_pushliteral(L, LLTHREAD_VERSION_COMMENT); + return 4; + } +#endif + return 3; +} + +static int l_llthread_push_version(lua_State *L){ + lua_pushnumber(L, LLTHREAD_VERSION_MAJOR); + lua_pushliteral(L, "."); + lua_pushnumber(L, LLTHREAD_VERSION_MINOR); + lua_pushliteral(L, "."); + lua_pushnumber(L, LLTHREAD_VERSION_PATCH); +#ifdef LLTHREAD_VERSION_COMMENT + if(LLTHREAD_VERSION_COMMENT[0]){ + lua_pushliteral(L, "-"LLTHREAD_VERSION_COMMENT); + lua_concat(L, 6); + } + else +#endif + lua_concat(L, 5); + return 1; +} + +//} + +static int l_llthread_set_logger(lua_State *L){ + lua_settop(L, 1); + luaL_argcheck(L, lua_isfunction(L, 1), 1, "function expected"); + lua_rawsetp(L, LUA_REGISTRYINDEX, LLTHREAD_LOGGER_HOLDER); + return 0; +} + +static const struct luaL_Reg l_llthreads_lib[] = { + {"new", l_llthread_new }, + {"set_logger", l_llthread_set_logger }, + {"version", l_llthread_version }, + + {NULL, NULL} +}; + +LLTHREADS_EXPORT_API int LLTHREAD_OPEN_NAME(lua_State *L) { + int top = lua_gettop(L); + lutil_createmetap(L, LLTHREAD_TAG, l_llthread_meth, 0); + lua_settop(L, top); + + lua_newtable(L); + luaL_setfuncs(L, l_llthreads_lib, 0); + + lua_pushliteral(L, "_VERSION"); + l_llthread_push_version(L); + lua_rawset(L, -3); + + return 1; +} diff --git a/llthreads2/src/traceback.inc b/llthreads2/src/traceback.inc new file mode 100644 index 0000000..af2f5a1 --- /dev/null +++ b/llthreads2/src/traceback.inc @@ -0,0 +1,56 @@ +/****************************************************************************** +* traceback() function from Lua 5.1/5.2 source. +* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ +#if !defined(LUA_VERSION_NUM) || (LUA_VERSION_NUM == 501) +/* from Lua 5.1 */ +static int traceback (lua_State *L) { + if (!lua_isstring(L, 1)) /* 'message' not a string? */ + return 1; /* keep it intact */ + lua_getglobal(L, "debug"); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return 1; + } + lua_getfield(L, -1, "traceback"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 2); + return 1; + } + lua_pushvalue(L, 1); /* pass error message */ + lua_pushinteger(L, 2); /* skip this function and traceback */ + lua_call(L, 2, 1); /* call debug.traceback */ + return 1; +} +#else +/* from Lua 5.2 */ +static int traceback (lua_State *L) { + const char *msg = lua_tostring(L, 1); + if (msg) + luaL_traceback(L, L, msg, 1); + else if (!lua_isnoneornil(L, 1)) { /* is there an error object? */ + if (!luaL_callmeta(L, 1, "__tostring")) /* try its 'tostring' metamethod */ + lua_pushliteral(L, "(no error message)"); + } + return 1; +} +#endif diff --git a/llthreads2/test/test_alive.lua b/llthreads2/test/test_alive.lua new file mode 100644 index 0000000..ecce163 --- /dev/null +++ b/llthreads2/test/test_alive.lua @@ -0,0 +1,35 @@ +local llthreads = require"llthreads" +local utils = require "utils" +local sleep = utils.sleep + +local include = utils.thread_init .. [[ +local llthreads = require"llthreads" +local sleep = require "utils".sleep +]] + +local thread = llthreads.new(include .. [[ + sleep(5) + return 1,2,3 +]]) + +assert(nil == thread:alive()) + +thread:start() + +assert(true == thread:alive()) + +for i = 1, 10 do + if not thread:alive() then break end + sleep(1) +end + +assert(false == thread:alive()) + +local ok,a,b,c = thread:join(0) +assert(ok == true) +assert(a == 1) +assert(b == 2) +assert(c == 3) + +print("Done!") + diff --git a/llthreads2/test/test_join_detach.lua b/llthreads2/test/test_join_detach.lua new file mode 100644 index 0000000..533a36f --- /dev/null +++ b/llthreads2/test/test_join_detach.lua @@ -0,0 +1,59 @@ +local llthreads = require"llthreads" +local utils = require "utils" + +do + +local thread = llthreads.new(utils.thread_init .. [[ + local sleep = require"utils".sleep + while true do sleep(1) end +]]) + +-- detached + joindable +thread:start(true, true) + +local ok, err = thread:join(0) +print("thread:join(0): ", ok, err) +assert(ok == nil) +assert(err == "timeout") + +end + +-- enforce collect `thread` object +-- we should not hungup +for i = 1, 10 do collectgarbage("collect") end + + +do + +local thread = llthreads.new(utils.thread_init .. [[ + local sleep = require"utils".sleep + sleep(1) +]]) + +-- detached + joindable +thread:start(true, true) + +local ok, err = thread:join(0) +print("thread:join(0): ", ok, err) +assert(ok == nil) +assert(err == "timeout") + +for i = 1, 12 do + utils.sleep(5) + ok, err = thread:join(0) + print("thread:join(0)#" .. i .. ": ", ok, err) + if ok then break end + assert(err == 'timeout') +end + +assert(ok) + +end + +-- enforce collect `thread` object +-- we should not get av +for i = 1, 10 do collectgarbage("collect") end + + +print("Done!") + diff --git a/llthreads2/test/test_join_error.lua b/llthreads2/test/test_join_error.lua new file mode 100644 index 0000000..90780b1 --- /dev/null +++ b/llthreads2/test/test_join_error.lua @@ -0,0 +1,24 @@ +local llthreads = require "llthreads" +local utils = require "utils" +local sleep = utils.sleep + +local include = utils.thread_init .. [[ +local llthreads = require"llthreads" +local sleep = require "utils".sleep +]] + +local thread = llthreads.new(include .. [[ + sleep(5) +]]) + +thread:start() + +local ok, err = thread:join() +assert(ok == true) +assert(err == nil) + +local res, ok, err = pcall(thread.join, thread) +assert(res == true) +assert(ok == nil) +assert(err ~= nil) + diff --git a/llthreads2/test/test_join_timeout.lua b/llthreads2/test/test_join_timeout.lua new file mode 100644 index 0000000..b9cf155 --- /dev/null +++ b/llthreads2/test/test_join_timeout.lua @@ -0,0 +1,23 @@ +local llthreads = require"llthreads" +local utils = require "utils" +local sleep = utils.sleep + +local include = utils.thread_init .. [[ +local llthreads = require"llthreads" +local sleep = require "utils".sleep +]] + +local thread = llthreads.new(include .. [[ + sleep(5) +]]) +thread:start() +local ok, err = thread:join(0) +print("thread:join(0): ", ok, err) +assert(ok == nil) +assert(err == "timeout") + +local ok, err = thread:join() +print("thread:join(): ", ok, err) +assert(ok, err) +print("Done!") + diff --git a/llthreads2/test/test_llthreads.lua b/llthreads2/test/test_llthreads.lua new file mode 100644 index 0000000..e0e214b --- /dev/null +++ b/llthreads2/test/test_llthreads.lua @@ -0,0 +1,60 @@ +-- Copyright (c) 2011 by Robert G. Jakabosky +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. + +local llthreads = require"llthreads" +local sleep = require"utils".sleep + +print("LLThreads version : ", llthreads._VERSION) + +local function detached_thread(...) + local thread = llthreads.new([[ print("print_detached_thread:", ...) ]], ...) + -- start detached thread + assert(thread:start(true)) + return thread +end + +local function print_thread(...) + local thread = llthreads.new([[ print("print_thread:", ...); ]], ...) + -- start joinable thread + assert(thread:start()) + return thread +end + +local function pass_through_thread(...) + local thread = llthreads.new([[ return "pass_thread:", ... ]], ...) + -- start joinable thread + assert(thread:start()) + return thread +end + +local thread1 = detached_thread("number:", 1234, "nil:", nil, "bool:", true) + +sleep(1) + +local thread2 = print_thread("number:", 1234, "nil:", nil, "bool:", true) +print("thread2:join: results # = ", select('#', thread2:join())) + +sleep(1) + +local thread3 = pass_through_thread("number:", 1234, "nil:", nil, "bool:", true) +print("thread3:join:", thread3:join()) + +sleep(1) + diff --git a/llthreads2/test/test_load_llthreads2.lua b/llthreads2/test/test_load_llthreads2.lua new file mode 100644 index 0000000..11bf0a0 --- /dev/null +++ b/llthreads2/test/test_load_llthreads2.lua @@ -0,0 +1,7 @@ +local llthreads = require"llthreads2" + +llthreads.new([[ + local os = require "os" + print("Done!") + os.exit(0) +]]):start():join() \ No newline at end of file diff --git a/llthreads2/test/test_logger.lua b/llthreads2/test/test_logger.lua new file mode 100644 index 0000000..f85a896 --- /dev/null +++ b/llthreads2/test/test_logger.lua @@ -0,0 +1,24 @@ +local utils = require "utils" + +require "llthreads".new(utils.thread_init .. [[ +require "string" + +require "llthreads".set_logger(function(msg) + if type(msg) ~= 'string' then + print("ERROR! Invalid error message: ", msg) + os.exit(-2) + end + if not msg:find("SOME ERROR", nil, true) then + print("ERROR! Invalid error message: ", msg) + os.exit(-1) + end + print("Done!") + os.exit(0) +end) + +error("SOME ERROR") +]]):start():join() + +print("ERROR! Logger has not been call!") +os.exit(-1) + diff --git a/llthreads2/test/test_pass_cfunction.lua b/llthreads2/test/test_pass_cfunction.lua new file mode 100644 index 0000000..86fcd3d --- /dev/null +++ b/llthreads2/test/test_pass_cfunction.lua @@ -0,0 +1,17 @@ +local llthreads = require"llthreads" +local utils = require"utils" + +local thread = llthreads.new(utils.thread_init .. [[ + require "llthreads" + local fn = ... + + if type(fn) ~= 'function' then + print("ERROR! No function : ", fn, type(fn)) + os.exit(-2) + end + + fn("print('Done!'); require'os'.exit(0)"):start():join() +]], llthreads.new) + +print(thread:start():join()) +os.exit(-1) \ No newline at end of file diff --git a/llthreads2/test/test_register_ffi.lua b/llthreads2/test/test_register_ffi.lua new file mode 100644 index 0000000..e98167f --- /dev/null +++ b/llthreads2/test/test_register_ffi.lua @@ -0,0 +1,14 @@ +if jit then + local llthreads = require "llthreads" + local thread = llthreads.new([[ + if not package.preload.ffi then + print("ffi does not register in thread") + os.exit(-1) + end + local ok, err = pcall(require, "ffi") + if not ok then + print("can not load ffi: ", err) + os.exit(-2) + end + ]]):start():join() +end diff --git a/llthreads2/test/test_register_llthreads.lua b/llthreads2/test/test_register_llthreads.lua new file mode 100644 index 0000000..5b234a9 --- /dev/null +++ b/llthreads2/test/test_register_llthreads.lua @@ -0,0 +1,15 @@ +-- Test if you build module with +-- LLTHREAD_REGISTER_THREAD_LIBRARY + +local llthreads = require "llthreads" +local thread = llthreads.new([[ + if not package.preload.llthreads then + print("llthreads does not register in thread") + os.exit(-1) + end + local ok, err = pcall(require, "llthreads") + if not ok then + print("can not load llthreads: ", err) + os.exit(-2) + end +]]):start():join() diff --git a/llthreads2/test/test_table_copy.lua b/llthreads2/test/test_table_copy.lua new file mode 100644 index 0000000..0408ad3 --- /dev/null +++ b/llthreads2/test/test_table_copy.lua @@ -0,0 +1,134 @@ +-- Copyright (c) 2011 by Robert G. Jakabosky +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. + +local llthreads = require"llthreads" + +local sleep +local status, socket = pcall(require,"socket") +if status then + sleep = function(secs) + return socket.sleep(secs) + end +else + sleep = function(secs) + os.execute("sleep " .. tonumber(secs)) + end +end + +local dump_code = [==[ +local function dump_recur(seen, obj, depth) + local out + local t = type(obj) + -- if not a table just convert to string. + if t ~= "table" then + if t == "string" then + return '"' .. obj .. '"' + end + return tostring(obj) + end + -- check if this table has been seen already. + if seen[obj] then + return "Already dumped " .. tostring(obj) + end + seen[obj] = true + -- restrict max depth. + if depth >= 10 then + return "{... max depth reached ...}" + end + depth = depth + 1 + -- output table key/value pairs + local tabs = string.rep(" ",depth) + local out = "{\n" + for k,v in pairs(obj) do + if type(k) ~= "number" then + out = out .. tabs .. '[' .. dump_recur(seen, k, depth) .. '] = ' .. + dump_recur(seen, v, depth) .. ',\n' + else + out = out .. tabs .. '[' .. k .. '] = ' .. dump_recur(seen, v, depth) .. ',\n' + end + end + return out .. tabs:sub(1,-3) .. "}" +end + +local obj = ... +local seen = {} +return dump_recur(seen, obj, 0) +]==] + +local dump = (loadstring or load)(dump_code) + +local child_code = [==[ +local dump = (loadstring or load)[[ +]==] .. dump_code .. [==[ +]] +local args = ... + +print("Child thread args:", dump(args)) + +-- return all values. +return ... +]==] + +local function test_thread_value_copying(...) + local args = {...} + print("Main thread args:", dump(args)) + local thread = llthreads.new(child_code, args) + -- start joinable thread + assert(thread:start()) + + local status, results = thread:join() + print("Main thread results:", dump(results)) +end + +-- create some tables. +local a1 = { "a1" } +local a2 = { "a2" } +local a3 = { "a3" } +local a4 = { "a4" } +local b1 = { a1, a2, a3, a4 } +local b2 = { a1=a1, a2=a2, a3=a3, a4=a4 } + +-- +-- no loops +-- +test_thread_value_copying(b1, b2) + +local top = {} +-- self reference. +top.top = top +top[top] = top +-- nested reference. +top.sub1 = { sub2 = { sub3 = { top } } } + +-- +-- loops +-- +test_thread_value_copying(top) + +-- +-- Test max depth +-- +local outer = {} +for n=1,100 do + outer = {outer} +end +local status, err = pcall(test_thread_value_copying,outer) +assert(not status, "Assertion failed: max depth test failed.") + diff --git a/llthreads2/test/test_threads.lua b/llthreads2/test/test_threads.lua new file mode 100644 index 0000000..459604d --- /dev/null +++ b/llthreads2/test/test_threads.lua @@ -0,0 +1,67 @@ +-- Copyright (c) 2011 by Ross Anderson +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. + +-- Sub-thread processing example in Lua using llthreads - 1,000 quick sub-thread execution + +-- luajit sub_threads.lua + +local llthreads = require"llthreads" +local utils = require "utils" + +local num_threads = tonumber(arg[1] or 1000) + +-- level 0 string literal enclosure [[ ]] of child execution code +local thread_code = utils.thread_init .. [[ + + local num_threads = ... + print("CHILD: received from ROOT params:", ...) + local llthreads = require"llthreads" -- need to re-declare this under this scope + local t = {} -- thread storage table + + -- create a new child sub-thread execution code - it requires level 1 literal string [=[ ]=] enclosures, level 2 would be [==[ ]==] + local executed_child_code = [=[ + return "Hello from child sub-thread, new input params:", ... + ]=] + + -- create 1000 sub-threads - which creates an incremental 30% / 20% utilization spike on the two AMD cpu cores + print("CHILD: Create sub threads:", num_threads) + for i=1,num_threads do + -- create child sub-thread with code to execute and the input parmeters + local thread = llthreads.new(executed_child_code , "number:", 1000 + i, "nil:", nil, "bool:", true) + assert(thread:start()) -- start new child sub-thread + table.insert(t, thread) -- append the thread at the end of the thread table + end + + -- wait (block) for all child sub-threads to complete before returning to ROOT + while true do + -- always wait on the first element, since order is not important + print("CHILD: sub-thread returned: ", t[1]:join()) + table.remove(t,1) -- always remove the first element + if (#t == 0) then break end + end + return ... -- return the parents' input params back to the root +]] + +-- create child thread. +local thread = llthreads.new(thread_code, num_threads, "number:", 1000, "nil:", nil, "bool:", true) +-- start joinable child thread. +assert(thread:start()) +-- wait for all child and child sub-threads to finish +print("ROOT: child returned: ", thread:join()) diff --git a/llthreads2/test/utils.lua b/llthreads2/test/utils.lua new file mode 100644 index 0000000..d9b5fe5 --- /dev/null +++ b/llthreads2/test/utils.lua @@ -0,0 +1,63 @@ +local lua_version_t +local function lua_version() + if not lua_version_t then + local version = rawget(_G,"_VERSION") + local maj,min = version:match("^Lua (%d+)%.(%d+)$") + if maj then lua_version_t = {tonumber(maj),tonumber(min)} + elseif not math.mod then lua_version_t = {5,2} + elseif table.pack and not pack then lua_version_t = {5,2} + else lua_version_t = {5,2} end + end + return lua_version_t[1], lua_version_t[2] +end + +local LUA_MAJOR, LUA_MINOR = lua_version() +local IS_LUA_51 = (LUA_MAJOR == 5) and (LUA_MINOR == 1) +local IS_LUA_52 = (LUA_MAJOR == 5) and (LUA_MINOR == 2) + +local LUA_INIT = "LUA_INIT" +local LUA_INIT_VER +if not IS_LUA_51 then + LUA_INIT_VER = LUA_INIT .. "_" .. LUA_MAJOR .. "_" .. LUA_MINOR +end + +LUA_INIT = LUA_INIT_VER and os.getenv( LUA_INIT_VER ) or os.getenv( LUA_INIT ) or "" + +LUA_INIT = [[do + local lua_init = ]] .. ("%q"):format(LUA_INIT) .. [[ + if lua_init and #lua_init > 0 then + if lua_init:sub(1,1) == '@' then + dofile(lua_init:sub(2)) + else + assert((loadstring or load)(lua_init))() + end + end +end;]] + +local sleep +local status, socket = pcall(require,"socket") +if status then + sleep = function(secs) + return socket.sleep(secs) + end +end + +if not sleep then + local status, ztimer = pcall(require, "lzmq.timer") + if status then + sleep = function(secs) + ztimer.sleep(secs * 1000) + end + end +end + +if not sleep then + sleep = function(secs) + os.execute("sleep " .. tonumber(secs)) + end +end + +return { + thread_init = LUA_INIT, + sleep = sleep, +} -- cgit v1.2.3-55-g6feb