<commit_before>#include <cstdlib> // size_t
#include <string>
#include <iostream>
#include <stdint.h>

#include <tightdb/utilities.hpp>

#ifdef TIGHTDB_COMPILER_SSE
#  ifdef _MSC_VER
#    include <intrin.h>
#  else
#    include <cpuid.h>
#  endif
#endif

namespace {

#ifdef TIGHTDB_COMPILER_SSE
#  if defined TIGHTDB_COMPILER_AVX && defined __GNUC__
#    define _XCR_XFEATURE_ENABLED_MASK 0
inline unsigned long long _xgetbv(unsigned index)
{
#    if TIGHTDB_HAVE_AT_LEAST_GCC(4, 4)
    unsigned int eax, edx;
    __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
    return (static_cast<unsigned long long>(edx) << 32) | eax;
#    else
    static_cast<void>(index);
    return 0;
#    endif
}
#  endif
#endif

} // anonymous namespace

namespace tightdb {

signed char sse_support = -1;
signed char avx_support = -1;

void cpuid_init()
{
#ifdef TIGHTDB_COMPILER_SSE
    int cret;
#  ifdef _MSC_VER
    int CPUInfo[4];
    __cpuid(CPUInfo, 1);
    cret = CPUInfo[2];
#  else
    int a = 1;
    __asm ( "mov %1, %%eax; "            // a into eax
          "cpuid;"
          "mov %%ecx, %0;"             // ecx into b
          :"=r"(cret)                     // output
          :"r"(a)                      // input
          :"%eax","%ebx","%ecx","%edx" // clobbered register
         );
#  endif

// Byte is atomic. Race can/will occur but that's fine
    if (cret & 0x100000) { // test for 4.2
        sse_support = 1;
    }
    else if (cret & 0x1) { // Test for 3
        sse_support = 0;
    }
    else {
        sse_support = -2;
    }

    bool avxSupported = false;

// seems like in jenkins builds, __GNUC__ is defined for clang?! todo fixme
#  if !defined __clang__ && ((_MSC_FULL_VER >= 160040219) || defined __GNUC__)
    bool osUsesXSAVE_XRSTORE = cret & (1 << 27) || false;
    bool cpuAVXSuport = cret & (1 << 28) || false;

    if (osUsesXSAVE_XRSTORE && cpuAVXSuport) {
        // Check if the OS will save the YMM registers
        unsigned long long xcrFeatureMask = _xgetbv(_XCR_XFEATURE_ENABLED_MASK);
        avxSupported = (xcrFeatureMask & 0x6) || false;
    }
#  endif

    if (avxSupported) {
        avx_support = 0; // AVX1 supported
    }
    else {
        avx_support = -1; // No AVX supported
    }

    // 1 is reserved for AVX2

#endif
}

// FIXME: Move all these rounding functions to the header file to
// allow inlining.
void* round_up(void* p, size_t align)
{
    // FIXME: The C++ standard does not guarantee that a pointer can
    // be stored in size_t. Use uintptr_t instead. The problem with
    // uintptr_t, is that is is not part of C++03.
    size_t r = size_t(p) % align == 0 ? 0 : align - size_t(p) % align;
    return static_cast<char *>(p) + r;
}

void* round_down(void* p, size_t align)
{
    // FIXME: The C++ standard does not guarantee that a pointer can
    // be stored in size_t. Use uintptr_t instead. The problem with
    // uintptr_t, is that is is not part of C++03.
    size_t r = size_t(p);
    return reinterpret_cast<void *>(r & ~(align - 1));
}

size_t round_up(size_t p, size_t align)
{
    size_t r = p % align == 0 ? 0 : align - p % align;
    return p + r;
}

size_t round_down(size_t p, size_t align)
{
    size_t r = p;
    return r & (~(align - 1));
}

void checksum_init(checksum_t* t)
{
    t->remainder = 0;
    t->remainder_len = 0;
    t->b_val = 0x794e80091e8f2bc7ULL;
    t->a_val = 0xc20f9a8b761b7e4cULL;
    t->result = 0;
}

unsigned long long checksum(unsigned char* data, size_t len)
{
    checksum_t t;
    checksum_init(&t);
    checksum_rolling(data, len, &t);
    return t.result;
}

void checksum_rolling(unsigned char* data, size_t len, checksum_t* t)
{
    while (t->remainder_len < 8 && len > 0) {
        t->remainder = t->remainder >> 8;
        t->remainder = t->remainder | static_cast<unsigned long long>(*data) << (7*8);
        t->remainder_len++;
        data++;
        len--;
    }

    if (t->remainder_len < 8) {
        t->result = t->a_val + t->b_val;
        return;
    }

    t->a_val += t->remainder * t->b_val;
    t->b_val++;
    t->remainder_len = 0;
    t->remainder = 0;

    while (len >= 8) {
#ifdef TIGHTDB_X86_OR_X64
        t->a_val += (*reinterpret_cast<unsigned long long*>(data)) * t->b_val;
#else
        unsigned long long l = 0;
        for (unsigned int i = 0; i < 8; i++) {
            l = l >> 8;
            l = l | static_cast<unsigned long long>(*(data + i)) << (7*8);
        }
        t->a_val += l * t->b_val;
#endif
        t->b_val++;
        len -= 8;
        data += 8;
    }

    while (len > 0) {
        t->remainder = t->remainder >> 8;
        t->remainder = t->remainder | static_cast<unsigned long long>(*data) << (7*8);
        t->remainder_len++;
        data++;
        len--;
    }

    t->result = t->a_val + t->b_val;
}

} // namespace tightdb

// popcount, counts number of set (1) bits in argument. Intrinsics has been disabled because it's just 10-20% faster
// than fallback method, so a runtime-detection of support would be more expensive in total. Popcount is supported
// with SSE42 but not with SSE3, and we don't want separate builds for each architecture - hence a runtime check would
// be required.
#if 0 // defined(_MSC_VER) && _MSC_VER >= 1500
#  include <intrin.h>

namespace tightdb {

int fast_popcount32(int32_t x)
{
    return __popcnt(x);
}
#  if defined(_M_X64)
int fast_popcount64(int64_t x)
{
    return int(__popcnt64(x));
}
#  else
int fast_popcount64(int64_t x)
{
    return __popcnt(unsigned(x)) + __popcnt(unsigned(x >> 32));
}
#  endif

} // namespace tightdb

#elif 0 // defined(__GNUC__) && __GNUC__ >= 4 || defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 900
#  define fast_popcount32 __builtin_popcount

namespace tightdb {

#  if ULONG_MAX == 0xffffffff
int fast_popcount64(int64_t x)
{
    return __builtin_popcount(unsigned(x)) + __builtin_popcount(unsigned(x >> 32));
}
#  else
int fast_popcount64(int64_t x)
{
    return __builtin_popcountll(x);
}
#  endif

} // namespace tightdb

#else

namespace {

const char a_popcount_bits[256] = {
    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,        4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
};

} // anonymous namespace

namespace tightdb {

// Masking away bits might be faster than bit shifting (which can be slow). Note that the compiler may optimize this automatically. Todo, investigate.
int fast_popcount32(int32_t x)
{
    return a_popcount_bits[255 & x] + a_popcount_bits[255 & x>> 8] + a_popcount_bits[255 & x>>16] + a_popcount_bits[255 & x>>24];
}
int fast_popcount64(int64_t x)
{
    return fast_popcount32(static_cast<int32_t>(x)) + fast_popcount32(static_cast<int32_t>(x >> 32));
}

} // namespace tightdb

#endif // select best popcount implementations
<commit_msg>Another fix for: Fixing https://app.asana.com/0/7119930778565/8981425391241 and doing quite a bit of cleanup in utilities.hpp + utilities.cpp<commit_after>#include <cstdlib> // size_t
#include <string>
#include <iostream>
#include <stdint.h>

#include <tightdb/utilities.hpp>

#ifdef TIGHTDB_COMPILER_SSE
#  ifdef _MSC_VER
#    include <intrin.h>
#  else
#    include <cpuid.h>
#  endif
#endif

namespace {

#ifdef TIGHTDB_COMPILER_SSE
#  if !defined __clang__ && ((_MSC_FULL_VER >= 160040219) || defined __GNUC__)
#    if defined TIGHTDB_COMPILER_AVX && defined __GNUC__
#      define _XCR_XFEATURE_ENABLED_MASK 0

inline unsigned long long _xgetbv(unsigned index)
{
#if TIGHTDB_HAVE_AT_LEAST_GCC(4, 4)
    unsigned int eax, edx;
    __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
    return (static_cast<unsigned long long>(edx) << 32) | eax;
#else
    static_cast<void>(index);
    return 0;
#endif
}

#    endif
#  endif
#endif

} // anonymous namespace

namespace tightdb {

signed char sse_support = -1;
signed char avx_support = -1;

void cpuid_init()
{
#ifdef TIGHTDB_COMPILER_SSE
    int cret;
#  ifdef _MSC_VER
    int CPUInfo[4];
    __cpuid(CPUInfo, 1);
    cret = CPUInfo[2];
#  else
    int a = 1;
    __asm ( "mov %1, %%eax; "            // a into eax
          "cpuid;"
          "mov %%ecx, %0;"             // ecx into b
          :"=r"(cret)                     // output
          :"r"(a)                      // input
          :"%eax","%ebx","%ecx","%edx" // clobbered register
         );
#  endif

// Byte is atomic. Race can/will occur but that's fine
    if (cret & 0x100000) { // test for 4.2
        sse_support = 1;
    }
    else if (cret & 0x1) { // Test for 3
        sse_support = 0;
    }
    else {
        sse_support = -2;
    }

    bool avxSupported = false;

// seems like in jenkins builds, __GNUC__ is defined for clang?! todo fixme
#  if !defined __clang__ && ((_MSC_FULL_VER >= 160040219) || defined __GNUC__)
    bool osUsesXSAVE_XRSTORE = cret & (1 << 27) || false;
    bool cpuAVXSuport = cret & (1 << 28) || false;

    if (osUsesXSAVE_XRSTORE && cpuAVXSuport) {
        // Check if the OS will save the YMM registers
        unsigned long long xcrFeatureMask = _xgetbv(_XCR_XFEATURE_ENABLED_MASK);
        avxSupported = (xcrFeatureMask & 0x6) || false;
    }
#  endif

    if (avxSupported) {
        avx_support = 0; // AVX1 supported
    }
    else {
        avx_support = -1; // No AVX supported
    }

    // 1 is reserved for AVX2

#endif
}

// FIXME: Move all these rounding functions to the header file to
// allow inlining.
void* round_up(void* p, size_t align)
{
    // FIXME: The C++ standard does not guarantee that a pointer can
    // be stored in size_t. Use uintptr_t instead. The problem with
    // uintptr_t, is that is is not part of C++03.
    size_t r = size_t(p) % align == 0 ? 0 : align - size_t(p) % align;
    return static_cast<char *>(p) + r;
}

void* round_down(void* p, size_t align)
{
    // FIXME: The C++ standard does not guarantee that a pointer can
    // be stored in size_t. Use uintptr_t instead. The problem with
    // uintptr_t, is that is is not part of C++03.
    size_t r = size_t(p);
    return reinterpret_cast<void *>(r & ~(align - 1));
}

size_t round_up(size_t p, size_t align)
{
    size_t r = p % align == 0 ? 0 : align - p % align;
    return p + r;
}

size_t round_down(size_t p, size_t align)
{
    size_t r = p;
    return r & (~(align - 1));
}

void checksum_init(checksum_t* t)
{
    t->remainder = 0;
    t->remainder_len = 0;
    t->b_val = 0x794e80091e8f2bc7ULL;
    t->a_val = 0xc20f9a8b761b7e4cULL;
    t->result = 0;
}

unsigned long long checksum(unsigned char* data, size_t len)
{
    checksum_t t;
    checksum_init(&t);
    checksum_rolling(data, len, &t);
    return t.result;
}

void checksum_rolling(unsigned char* data, size_t len, checksum_t* t)
{
    while (t->remainder_len < 8 && len > 0) {
        t->remainder = t->remainder >> 8;
        t->remainder = t->remainder | static_cast<unsigned long long>(*data) << (7*8);
        t->remainder_len++;
        data++;
        len--;
    }

    if (t->remainder_len < 8) {
        t->result = t->a_val + t->b_val;
        return;
    }

    t->a_val += t->remainder * t->b_val;
    t->b_val++;
    t->remainder_len = 0;
    t->remainder = 0;

    while (len >= 8) {
#ifdef TIGHTDB_X86_OR_X64
        t->a_val += (*reinterpret_cast<unsigned long long*>(data)) * t->b_val;
#else
        unsigned long long l = 0;
        for (unsigned int i = 0; i < 8; i++) {
            l = l >> 8;
            l = l | static_cast<unsigned long long>(*(data + i)) << (7*8);
        }
        t->a_val += l * t->b_val;
#endif
        t->b_val++;
        len -= 8;
        data += 8;
    }

    while (len > 0) {
        t->remainder = t->remainder >> 8;
        t->remainder = t->remainder | static_cast<unsigned long long>(*data) << (7*8);
        t->remainder_len++;
        data++;
        len--;
    }

    t->result = t->a_val + t->b_val;
}

} // namespace tightdb

// popcount, counts number of set (1) bits in argument. Intrinsics has been disabled because it's just 10-20% faster
// than fallback method, so a runtime-detection of support would be more expensive in total. Popcount is supported
// with SSE42 but not with SSE3, and we don't want separate builds for each architecture - hence a runtime check would
// be required.
#if 0 // defined(_MSC_VER) && _MSC_VER >= 1500
#  include <intrin.h>

namespace tightdb {

int fast_popcount32(int32_t x)
{
    return __popcnt(x);
}
#  if defined(_M_X64)
int fast_popcount64(int64_t x)
{
    return int(__popcnt64(x));
}
#  else
int fast_popcount64(int64_t x)
{
    return __popcnt(unsigned(x)) + __popcnt(unsigned(x >> 32));
}
#  endif

} // namespace tightdb

#elif 0 // defined(__GNUC__) && __GNUC__ >= 4 || defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 900
#  define fast_popcount32 __builtin_popcount

namespace tightdb {

#  if ULONG_MAX == 0xffffffff
int fast_popcount64(int64_t x)
{
    return __builtin_popcount(unsigned(x)) + __builtin_popcount(unsigned(x >> 32));
}
#  else
int fast_popcount64(int64_t x)
{
    return __builtin_popcountll(x);
}
#  endif

} // namespace tightdb

#else

namespace {

const char a_popcount_bits[256] = {
    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,        4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
};

} // anonymous namespace

namespace tightdb {

// Masking away bits might be faster than bit shifting (which can be slow). Note that the compiler may optimize this automatically. Todo, investigate.
int fast_popcount32(int32_t x)
{
    return a_popcount_bits[255 & x] + a_popcount_bits[255 & x>> 8] + a_popcount_bits[255 & x>>16] + a_popcount_bits[255 & x>>24];
}
int fast_popcount64(int64_t x)
{
    return fast_popcount32(static_cast<int32_t>(x)) + fast_popcount32(static_cast<int32_t>(x >> 32));
}

} // namespace tightdb

#endif // select best popcount implementations
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "partition_version.hh"

// Allows iterating over rows of mutation_partition represented by given partition_snapshot.
//
// The cursor initially has a position before all rows and is not pointing at any row.
// To position the cursor, use advance_to().
//
// All methods should be called with the region of the snapshot locked. The cursor is invalidated
// when that lock section is left, or if the snapshot is modified.
//
// When the cursor is invalidated, it still maintains its previous position. It can be brought
// back to validity by calling maybe_refresh(), or advance_to().
//
// Insertion of row entries after cursor's position invalidates the cursor.
//
class partition_snapshot_row_cursor final {
    struct position_in_version {
        mutation_partition::rows_type::iterator it;
        mutation_partition::rows_type::iterator end;
        int version_no;

        struct less_compare {
            rows_entry::tri_compare _cmp;
        public:
            explicit less_compare(const schema& s) : _cmp(s) { }
            bool operator()(const position_in_version& a, const position_in_version& b) {
                auto res = _cmp(*a.it, *b.it);
                return res > 0 || (res == 0 && a.version_no > b.version_no);
            }
        };
    };

    const schema& _schema;
    partition_snapshot& _snp;
    std::vector<position_in_version> _heap;
    std::vector<mutation_partition::rows_type::iterator> _iterators;
    std::vector<position_in_version> _current_row;
    position_in_partition _position;
    partition_snapshot::change_mark _change_mark;

    // Removes the next row from _heap and puts it into _current_row
    void recreate_current_row() {
        position_in_version::less_compare heap_less(_schema);
        position_in_partition::equal_compare eq(_schema);
        do {
            boost::range::pop_heap(_heap, heap_less);
            _current_row.push_back(_heap.back());
            _heap.pop_back();
        } while (!_heap.empty() && eq(_current_row[0].it->position(), _heap[0].it->position()));
        _position = position_in_partition(_current_row[0].it->position());
    }
public:
    partition_snapshot_row_cursor(const schema& s, partition_snapshot& snp)
        : _schema(s)
        , _snp(snp)
        , _position(position_in_partition::static_row_tag_t{})
    { }
    bool has_valid_row_from_latest_version() const {
        return iterators_valid() && _current_row[0].version_no == 0;
    }
    mutation_partition::rows_type::iterator get_iterator_in_latest_version() const {
        return _iterators[0];
    }

    // Returns true iff the iterators obtained since the cursor was last made valid
    // are still valid. Note that this doesn't mean that the cursor itself is valid.
    bool iterators_valid() const {
        return _snp.get_change_mark() == _change_mark;
    }

    // Brings back the cursor to validity.
    // Can be only called when cursor is pointing at a row.
    //
    // Semantically equivalent to:
    //
    //   advance_to(position());
    //
    // but avoids work if not necessary.
    bool maybe_refresh() {
        if (!iterators_valid()) {
            return advance_to(_position);
        }
        // Refresh latest version's iterator in case there was an insertion
        // before it and after cursor's position. There cannot be any
        // insertions for non-latest versions, so we don't have to update them.
        if (_current_row[0].version_no != 0) {
            rows_entry::compare less(_schema);
            position_in_partition::equal_compare eq(_schema);
            position_in_version::less_compare heap_less(_schema);
            auto& rows = _snp.version()->partition().clustered_rows();
            auto it = _iterators[0] = rows.lower_bound(_position, less);
            auto heap_i = boost::find_if(_heap, [](auto&& v) { return v.version_no == 0; });
            if (it == rows.end()) {
                if (heap_i != _heap.end()) {
                    _heap.erase(heap_i);
                    boost::range::make_heap(_heap, heap_less);
                }
            } else if (eq(_position, it->position())) {
                _current_row.insert(_current_row.begin(), position_in_version{it, rows.end(), 0});
                if (heap_i != _heap.end()) {
                    _heap.erase(heap_i);
                    boost::range::make_heap(_heap, heap_less);
                }
            } else {
                if (heap_i != _heap.end()) {
                    heap_i->it = it;
                    boost::range::make_heap(_heap, heap_less);
                } else {
                    _heap.push_back({it, rows.end(), 0});
                    boost::range::push_heap(_heap, heap_less);
                }
            }
        }
        return true;
    }

    // Moves the cursor to the first entry with position >= pos.
    //
    // The caller must ensure that such entry exists.
    //
    // Returns true iff there can't be any clustering row entries
    // between lower_bound (inclusive) and the entry to which the cursor
    // was advanced.
    //
    // May be called when cursor is not valid.
    // The cursor is valid after the call.
    // Must be called under reclaim lock.
    bool advance_to(position_in_partition_view lower_bound) {
        rows_entry::compare less(_schema);
        position_in_version::less_compare heap_less(_schema);
        _heap.clear();
        _current_row.clear();
        _iterators.clear();
        int version_no = 0;
        for (auto&& v : _snp.versions()) {
            auto& rows = v.partition().clustered_rows();
            auto pos = rows.lower_bound(lower_bound, less);
            auto end = rows.end();
            _iterators.push_back(pos);
            if (pos != end) {
                _heap.push_back({pos, end, version_no});
            }
            ++version_no;
        }
        boost::range::make_heap(_heap, heap_less);
        _change_mark = _snp.get_change_mark();
        bool found = no_clustering_row_between(_schema, lower_bound, _heap[0].it->position());
        recreate_current_row();
        return found;
    }

    // Advances the cursor to the next row.
    // If there is no next row, returns false and the cursor is no longer pointing at a row.
    // Can be only called on a valid cursor pointing at a row.
    bool next() {
        position_in_version::less_compare heap_less(_schema);
        assert(iterators_valid());
        for (auto&& curr : _current_row) {
            ++curr.it;
            _iterators[curr.version_no] = curr.it;
            if (curr.it != curr.end) {
                _heap.push_back(curr);
                boost::range::push_heap(_heap, heap_less);
            }
        }
        _current_row.clear();
        if (_heap.empty()) {
            return false;
        }
        recreate_current_row();
        return true;
    }

    // Can be called only when cursor is valid and pointing at a row.
    bool continuous() const { return bool(_current_row[0].it->continuous()); }

    // Can be called only when cursor is valid and pointing at a row.
    bool dummy() const { return bool(_current_row[0].it->dummy()); }

    // Can be called only when cursor is valid and pointing at a row, and !dummy().
    const clustering_key& key() const { return _current_row[0].it->key(); }

    // Can be called only when cursor is valid and pointing at a row.
    mutation_fragment row() const {
        auto it = _current_row.begin();
        auto mf = mutation_fragment(clustering_row(*it->it));
        auto& cr = mf.as_mutable_clustering_row();
        for (++it; it != _current_row.end(); ++it) {
            cr.apply(_schema, *it->it);
        }
        return mf;
    }

    // Can be called when cursor is pointing at a row, even when invalid.
    const position_in_partition& position() const {
        return _position;
    }

    bool is_in_latest_version() const;
    bool previous_row_in_latest_version_has_key(const clustering_key_prefix& key) const;
    void set_continuous(bool val);

    friend std::ostream& operator<<(std::ostream& out, const partition_snapshot_row_cursor& cur) {
        out << "{cursor: position=" << cur._position << ", ";
        if (!cur.iterators_valid()) {
            return out << " iterators invalid}";
        }
        out << "current=[";
        bool first = true;
        for (auto&& v : cur._current_row) {
            if (!first) {
                out << ", ";
            }
            first = false;
            out << v.version_no;
        }
        out << "], heap=[";
        first = true;
        for (auto&& v : cur._heap) {
            if (!first) {
                out << ", ";
            }
            first = false;
            out << "{v=" << v.version_no << ", pos=" << v.it->position() << "}";
        }
        return out << "]}";
    };
};

inline
bool partition_snapshot_row_cursor::is_in_latest_version() const {
    return _current_row[0].version_no == 0;
}

inline
bool partition_snapshot_row_cursor::previous_row_in_latest_version_has_key(const clustering_key_prefix& key) const {
    if (_current_row[0].it == _snp.version()->partition().clustered_rows().begin()) {
        return false;
    }
    auto prev_it = _current_row[0].it;
    --prev_it;
    clustering_key_prefix::equality eq(_schema);
    return eq(prev_it->key(), key);
}

inline
void partition_snapshot_row_cursor::set_continuous(bool val) {
    _current_row[0].it->set_continuous(val);
}
<commit_msg>mvcc: Introduce partition_snapshot_row_weakref<commit_after>/*
 * Copyright (C) 2017 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "partition_version.hh"

class partition_snapshot_row_cursor;

// A non-owning reference to a row inside partition_snapshot which
// maintains it's position and thus can be kept across reference invalidation points.
class partition_snapshot_row_weakref final {
    mutation_partition::rows_type::iterator _it;
    partition_snapshot::change_mark _change_mark;
    position_in_partition _pos = position_in_partition::min();
public:
    partition_snapshot_row_weakref() = default;
    // Makes this object point to a row pointed to by given partition_snapshot_row_cursor.
    explicit partition_snapshot_row_weakref(const partition_snapshot_row_cursor&) noexcept;
    explicit partition_snapshot_row_weakref(std::nullptr_t) {}
    partition_snapshot_row_weakref(partition_snapshot& snp, mutation_partition::rows_type::iterator it)
        : _it(it)
        , _change_mark(snp.get_change_mark())
        , _pos(it->position())
    { }
    partition_snapshot_row_weakref& operator=(const partition_snapshot_row_cursor&) noexcept;
    partition_snapshot_row_weakref& operator=(std::nullptr_t) noexcept {
        _change_mark = {};
        return *this;
    }
    // Returns true iff the pointer is pointing at a row.
    explicit operator bool() const { return _change_mark != partition_snapshot::change_mark(); }
public:
    // Returns the position of the row.
    // Call only when pointing at a row.
    const position_in_partition& position() const { return _pos; }
    // Returns true iff the object is valid.
    bool valid(partition_snapshot& snp) { return snp.get_change_mark() == _change_mark; }
    // Brings the object back to validity and returns true iff the snapshot contains the row.
    // When not pointing at a row, returns false.
    bool refresh(partition_snapshot& snp) {
        auto snp_cm = snp.get_change_mark();
        if (snp_cm == _change_mark) {
            return true;
        }
        if (!_change_mark) {
            return false;
        }
        _change_mark = snp_cm;
        rows_entry::compare less(*snp.schema());
        for (auto&& v : snp.versions()) {
            auto& rows = v.partition().clustered_rows();
            _it = rows.find(_pos, less);
            if (_it != rows.end()) {
                return true;
            }
        }
        return false;
    }
};

// Allows iterating over rows of mutation_partition represented by given partition_snapshot.
//
// The cursor initially has a position before all rows and is not pointing at any row.
// To position the cursor, use advance_to().
//
// All methods should be called with the region of the snapshot locked. The cursor is invalidated
// when that lock section is left, or if the snapshot is modified.
//
// When the cursor is invalidated, it still maintains its previous position. It can be brought
// back to validity by calling maybe_refresh(), or advance_to().
//
// Insertion of row entries after cursor's position invalidates the cursor.
//
class partition_snapshot_row_cursor final {
    friend class partition_snapshot_row_weakref;
    struct position_in_version {
        mutation_partition::rows_type::iterator it;
        mutation_partition::rows_type::iterator end;
        int version_no;

        struct less_compare {
            rows_entry::tri_compare _cmp;
        public:
            explicit less_compare(const schema& s) : _cmp(s) { }
            bool operator()(const position_in_version& a, const position_in_version& b) {
                auto res = _cmp(*a.it, *b.it);
                return res > 0 || (res == 0 && a.version_no > b.version_no);
            }
        };
    };

    const schema& _schema;
    partition_snapshot& _snp;
    std::vector<position_in_version> _heap;
    std::vector<mutation_partition::rows_type::iterator> _iterators;
    std::vector<position_in_version> _current_row;
    position_in_partition _position;
    partition_snapshot::change_mark _change_mark;

    // Removes the next row from _heap and puts it into _current_row
    void recreate_current_row() {
        position_in_version::less_compare heap_less(_schema);
        position_in_partition::equal_compare eq(_schema);
        do {
            boost::range::pop_heap(_heap, heap_less);
            _current_row.push_back(_heap.back());
            _heap.pop_back();
        } while (!_heap.empty() && eq(_current_row[0].it->position(), _heap[0].it->position()));
        _position = position_in_partition(_current_row[0].it->position());
    }
public:
    partition_snapshot_row_cursor(const schema& s, partition_snapshot& snp)
        : _schema(s)
        , _snp(snp)
        , _position(position_in_partition::static_row_tag_t{})
    { }
    bool has_valid_row_from_latest_version() const {
        return iterators_valid() && _current_row[0].version_no == 0;
    }
    mutation_partition::rows_type::iterator get_iterator_in_latest_version() const {
        return _iterators[0];
    }

    // Returns true iff the iterators obtained since the cursor was last made valid
    // are still valid. Note that this doesn't mean that the cursor itself is valid.
    bool iterators_valid() const {
        return _snp.get_change_mark() == _change_mark;
    }

    // Brings back the cursor to validity.
    // Can be only called when cursor is pointing at a row.
    //
    // Semantically equivalent to:
    //
    //   advance_to(position());
    //
    // but avoids work if not necessary.
    bool maybe_refresh() {
        if (!iterators_valid()) {
            return advance_to(_position);
        }
        // Refresh latest version's iterator in case there was an insertion
        // before it and after cursor's position. There cannot be any
        // insertions for non-latest versions, so we don't have to update them.
        if (_current_row[0].version_no != 0) {
            rows_entry::compare less(_schema);
            position_in_partition::equal_compare eq(_schema);
            position_in_version::less_compare heap_less(_schema);
            auto& rows = _snp.version()->partition().clustered_rows();
            auto it = _iterators[0] = rows.lower_bound(_position, less);
            auto heap_i = boost::find_if(_heap, [](auto&& v) { return v.version_no == 0; });
            if (it == rows.end()) {
                if (heap_i != _heap.end()) {
                    _heap.erase(heap_i);
                    boost::range::make_heap(_heap, heap_less);
                }
            } else if (eq(_position, it->position())) {
                _current_row.insert(_current_row.begin(), position_in_version{it, rows.end(), 0});
                if (heap_i != _heap.end()) {
                    _heap.erase(heap_i);
                    boost::range::make_heap(_heap, heap_less);
                }
            } else {
                if (heap_i != _heap.end()) {
                    heap_i->it = it;
                    boost::range::make_heap(_heap, heap_less);
                } else {
                    _heap.push_back({it, rows.end(), 0});
                    boost::range::push_heap(_heap, heap_less);
                }
            }
        }
        return true;
    }

    // Moves the cursor to the first entry with position >= pos.
    //
    // The caller must ensure that such entry exists.
    //
    // Returns true iff there can't be any clustering row entries
    // between lower_bound (inclusive) and the entry to which the cursor
    // was advanced.
    //
    // May be called when cursor is not valid.
    // The cursor is valid after the call.
    // Must be called under reclaim lock.
    bool advance_to(position_in_partition_view lower_bound) {
        rows_entry::compare less(_schema);
        position_in_version::less_compare heap_less(_schema);
        _heap.clear();
        _current_row.clear();
        _iterators.clear();
        int version_no = 0;
        for (auto&& v : _snp.versions()) {
            auto& rows = v.partition().clustered_rows();
            auto pos = rows.lower_bound(lower_bound, less);
            auto end = rows.end();
            _iterators.push_back(pos);
            if (pos != end) {
                _heap.push_back({pos, end, version_no});
            }
            ++version_no;
        }
        boost::range::make_heap(_heap, heap_less);
        _change_mark = _snp.get_change_mark();
        bool found = no_clustering_row_between(_schema, lower_bound, _heap[0].it->position());
        recreate_current_row();
        return found;
    }

    // Advances the cursor to the next row.
    // If there is no next row, returns false and the cursor is no longer pointing at a row.
    // Can be only called on a valid cursor pointing at a row.
    bool next() {
        position_in_version::less_compare heap_less(_schema);
        assert(iterators_valid());
        for (auto&& curr : _current_row) {
            ++curr.it;
            _iterators[curr.version_no] = curr.it;
            if (curr.it != curr.end) {
                _heap.push_back(curr);
                boost::range::push_heap(_heap, heap_less);
            }
        }
        _current_row.clear();
        if (_heap.empty()) {
            return false;
        }
        recreate_current_row();
        return true;
    }

    // Can be called only when cursor is valid and pointing at a row.
    bool continuous() const { return bool(_current_row[0].it->continuous()); }

    // Can be called only when cursor is valid and pointing at a row.
    bool dummy() const { return bool(_current_row[0].it->dummy()); }

    // Can be called only when cursor is valid and pointing at a row, and !dummy().
    const clustering_key& key() const { return _current_row[0].it->key(); }

    // Can be called only when cursor is valid and pointing at a row.
    mutation_fragment row() const {
        auto it = _current_row.begin();
        auto mf = mutation_fragment(clustering_row(*it->it));
        auto& cr = mf.as_mutable_clustering_row();
        for (++it; it != _current_row.end(); ++it) {
            cr.apply(_schema, *it->it);
        }
        return mf;
    }

    // Can be called when cursor is pointing at a row, even when invalid.
    const position_in_partition& position() const {
        return _position;
    }

    bool is_in_latest_version() const;
    bool previous_row_in_latest_version_has_key(const clustering_key_prefix& key) const;
    void set_continuous(bool val);

    friend std::ostream& operator<<(std::ostream& out, const partition_snapshot_row_cursor& cur) {
        out << "{cursor: position=" << cur._position << ", ";
        if (!cur.iterators_valid()) {
            return out << " iterators invalid}";
        }
        out << "current=[";
        bool first = true;
        for (auto&& v : cur._current_row) {
            if (!first) {
                out << ", ";
            }
            first = false;
            out << v.version_no;
        }
        out << "], heap=[";
        first = true;
        for (auto&& v : cur._heap) {
            if (!first) {
                out << ", ";
            }
            first = false;
            out << "{v=" << v.version_no << ", pos=" << v.it->position() << "}";
        }
        return out << "]}";
    };
};

inline
bool partition_snapshot_row_cursor::is_in_latest_version() const {
    return _current_row[0].version_no == 0;
}

inline
bool partition_snapshot_row_cursor::previous_row_in_latest_version_has_key(const clustering_key_prefix& key) const {
    if (_current_row[0].it == _snp.version()->partition().clustered_rows().begin()) {
        return false;
    }
    auto prev_it = _current_row[0].it;
    --prev_it;
    clustering_key_prefix::equality eq(_schema);
    return eq(prev_it->key(), key);
}

inline
void partition_snapshot_row_cursor::set_continuous(bool val) {
    _current_row[0].it->set_continuous(val);
}

inline
partition_snapshot_row_weakref::partition_snapshot_row_weakref(const partition_snapshot_row_cursor& c) noexcept
    : _it(c._current_row[0].it)
    , _change_mark(c._change_mark)
    , _pos(c._position)
{ }

inline
partition_snapshot_row_weakref& partition_snapshot_row_weakref::operator=(const partition_snapshot_row_cursor& c) noexcept {
    this->~partition_snapshot_row_weakref();
    new (this) partition_snapshot_row_weakref(c);
    return *this;
}
<|endoftext|>


<commit_before>/*
    Copyright 2011 Joe Hermaszewski. All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY JOE HERMASZEWSKI "AS IS" AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
    EVENT SHALL JOE HERMASZEWSKI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    The views and conclusions contained in the software and documentation are
    those of the authors and should not be interpreted as representing official
    policies, either expressed or implied, of Joe Hermaszewski.
*/

#include <iostream>
#include <string>

#include <engine/context.hpp>
#include <engine/effect.hpp>
#include <engine/state.hpp>

#include <joemath/joemath.hpp>

int main( int argc, char** argv )
{
    JoeLang::Context context;

    JoeLang::State<int> my_int_state( "my_int_state" );
    my_int_state.SetCallbacks( [](int v) -> void
                                 {std::cout << "setting my_int_state to "
                                            << v << std::endl;} );

    JoeLang::State<float> my_state( "my_floating_state",
                                    std::map<std::string,float>({{"one", 1}, {"two", 2}}) );

    my_state.SetCallbacks( [](float v) -> void
                             {std::cout << "setting my_floating_state to "
                                        << v << std::endl;} );

    JoeLang::State<bool> my_bool_state(
                                "my_bool_state",
                                std::map<std::string,bool>({{"on",true},
                                                            {"off", false}}) );
    my_bool_state.SetCallbacks( [](bool v) -> void
                                  {std::cout << "setting my_bool_state to "
                                             << v << std::endl;} );

    JoeLang::State<std::string> my_string_state(
                    "my_string_state",
                    std::map<std::string, std::string>({{"string_enumerant",
                                                         "string_enumerant"}}));
    my_string_state.SetCallbacks( [](std::string v) -> void
                                  {std::cout << "setting my_string_state to "
                                             << v << std::endl;} );

    JoeLang::State<JoeMath::float4> my_float4_state(
      "my_float4_state",
      std::map<std::string,JoeMath::float4>({{"ones", JoeMath::float4{1,1,1,1}},
                                             {"zeroes", JoeMath::float4(0)}}) );
    my_float4_state.SetCallbacks( [](JoeMath::float4 v) -> void
                                  {std::cout << "setting my_float4_state to "
                                             << v.x() << " " << v.y() << " "
                                             << v.z() << " " << v.w()
                                             << std::endl;} );

    context.AddState( &my_state );
    context.AddState( &my_int_state );
    context.AddState( &my_bool_state );
    context.AddState( &my_string_state );
    context.AddState( &my_float4_state );

    JoeLang::Effect* e = context.CreateEffectFromFile( "test.jfx" );

    //
    // Flush std::cerr (it's used for dumping the module)
    //
    std::cerr.flush();
    if( e )
    {
        const JoeLang::Technique* t = e->GetNamedTechnique( "t1" );
        if( t )
            for( const auto& pass : t->GetPasses() )
            {
                std::cout << "Setting Pass: \'"
                          << pass.GetName() << "\' state\n";
                pass.SetState();
                pass.ResetState();
            }
    }
    else
        std::cout << "fail\n";
}
<commit_msg>[~] fixed headers<commit_after>/*
    Copyright 2011 Joe Hermaszewski. All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY JOE HERMASZEWSKI "AS IS" AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
    EVENT SHALL JOE HERMASZEWSKI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    The views and conclusions contained in the software and documentation are
    those of the authors and should not be interpreted as representing official
    policies, either expressed or implied, of Joe Hermaszewski.
*/

#include <iostream>
#include <string>

#include <joelang/context.hpp>
#include <joelang/effect.hpp>
#include <joelang/state.hpp>

#include <joemath/joemath.hpp>

int main( int argc, char** argv )
{
    JoeLang::Context context;

    JoeLang::State<int> my_int_state( "my_int_state" );
    my_int_state.SetCallbacks( [](int v) -> void
                                 {std::cout << "setting my_int_state to "
                                            << v << std::endl;} );

    JoeLang::State<float> my_state( "my_floating_state",
                                    std::map<std::string,float>({{"one", 1}, {"two", 2}}) );

    my_state.SetCallbacks( [](float v) -> void
                             {std::cout << "setting my_floating_state to "
                                        << v << std::endl;} );

    JoeLang::State<bool> my_bool_state(
                                "my_bool_state",
                                std::map<std::string,bool>({{"on",true},
                                                            {"off", false}}) );
    my_bool_state.SetCallbacks( [](bool v) -> void
                                  {std::cout << "setting my_bool_state to "
                                             << v << std::endl;} );

    JoeLang::State<std::string> my_string_state(
                    "my_string_state",
                    std::map<std::string, std::string>({{"string_enumerant",
                                                         "string_enumerant"}}));
    my_string_state.SetCallbacks( [](std::string v) -> void
                                  {std::cout << "setting my_string_state to "
                                             << v << std::endl;} );

    JoeLang::State<JoeMath::float4> my_float4_state(
      "my_float4_state",
      std::map<std::string,JoeMath::float4>({{"ones", JoeMath::float4{1,1,1,1}},
                                             {"zeroes", JoeMath::float4(0)}}) );
    my_float4_state.SetCallbacks( [](JoeMath::float4 v) -> void
                                  {std::cout << "setting my_float4_state to "
                                             << v.x() << " " << v.y() << " "
                                             << v.z() << " " << v.w()
                                             << std::endl;} );

    context.AddState( &my_state );
    context.AddState( &my_int_state );
    context.AddState( &my_bool_state );
    context.AddState( &my_string_state );
    context.AddState( &my_float4_state );

    JoeLang::Effect* e = context.CreateEffectFromFile( "test.jfx" );

    //
    // Flush std::cerr (it's used for dumping the module)
    //
    std::cerr.flush();
    if( e )
    {
        const JoeLang::Technique* t = e->GetNamedTechnique( "t1" );
        if( t )
            for( const auto& pass : t->GetPasses() )
            {
                std::cout << "Setting Pass: \'"
                          << pass.GetName() << "\' state\n";
                pass.SetState();
                pass.ResetState();
            }
    }
    else
        std::cout << "fail\n";
}
<|endoftext|>


<commit_before>/*
 * Sets given environment variables, dumps the entire environment to
 * a given file (for diagnostics purposes), then execs the given command.
 *
 * This is a separate executable because it does quite
 * some non-async-signal-safe stuff that we can't do after
 * fork()ing from the Spawner and before exec()ing.
 */

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string>
#include <Utils/Base64.h>

using namespace std;
using namespace Passenger;

extern "C" {
	extern char **environ;
}

static void
changeWorkingDir(const char *dir) {
	int ret = chdir(dir);
	if (ret == 0) {
		setenv("PWD", dir, 1);
	} else {
		int e = errno;
		printf("!> Error\n");
		printf("!> \n");
		printf("Unable to change working directory to '%s': %s (errno=%d)\n",
			dir, strerror(e), e);
		fflush(stdout);
		exit(1);
	}
}

static void
setGivenEnvVars(const char *envvarsData) {
	string envvars = Base64::decode(envvarsData);
	const char *key = envvars.data();
	const char *end = envvars.data() + envvars.size();

	while (key < end) {
		const char *keyEnd = (const char *) memchr(key, '\0', end - key);
		if (keyEnd != NULL) {
			const char *value = keyEnd + 1;
			if (value < end) {
				const char *valueEnd = (const char *) memchr(value, '\0', end - value);
				if (valueEnd != NULL) {
					setenv(key, value, 1);
					key = valueEnd + 1;
				} else {
					break;
				}
			} else {
				break;
			}
		} else {
			break;
		}
	}
}

static void
dumpInformation() {
	const char *c_dir;
	if ((c_dir = getenv("PASSENGER_DEBUG_DIR")) == NULL) {
		return;
	}

	FILE *f;
	string dir = c_dir;

	f = fopen((dir + "/envvars").c_str(), "w");
	if (f != NULL) {
		int i = 0;
		while (environ[i] != NULL) {
			fputs(environ[i], f);
			putc('\n', f);
			i++;
		}
		fclose(f);
	}

	f = fopen((dir + "/user_info").c_str(), "w");
	if (f != NULL) {
		pid_t pid = fork();
		if (pid == 0) {
			dup2(fileno(f), 1);
			execlp("id", "id", (char *) 0);
			_exit(1);
		} else if (pid == -1) {
			int e = errno;
			fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
				strerror(e), e);
		} else {
			waitpid(pid, NULL, 0);
		}
		fclose(f);
	}

	f = fopen((dir + "/ulimit").c_str(), "w");
	if (f != NULL) {
		pid_t pid = fork();
		if (pid == 0) {
			dup2(fileno(f), 1);
			execlp("ulimit", "ulimit", "-a", (char *) 0);
			_exit(1);
		} else if (pid == -1) {
			int e = errno;
			fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
				strerror(e), e);
		} else {
			waitpid(pid, NULL, 0);
		}
		fclose(f);
	}

	f = fopen((dir + "/ulimit").c_str(), "w");
	if (f != NULL) {
		pid_t pid = fork();
		if (pid == 0) {
			dup2(fileno(f), 1);
			execlp("ulimit", "ulimit", "-a", (char *) 0);
			_exit(1);
		} else if (pid == -1) {
			int e = errno;
			fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
				strerror(e), e);
		} else {
			waitpid(pid, NULL, 0);
		}
		fclose(f);
	}

	#ifdef __linux__
		// TODO: call helper-scripts/system-memory-stats.py
		f = fopen((dir + "/sysmemory").c_str(), "w");
		if (f != NULL) {
			pid_t pid = fork();
			if (pid == 0) {
				dup2(fileno(f), 1);
				execlp("free", "free", "-m", (char *) 0);
				_exit(1);
			} else if (pid == -1) {
				int e = errno;
				fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
					strerror(e), e);
			} else {
				waitpid(pid, NULL, 0);
			}
			fclose(f);
		}
	#endif
}

// Usage: SpawnPreparer <working directory> <envvars> <executable> <exec args...>
int
main(int argc, char *argv[]) {
	if (argc < 5) {
		fprintf(stderr, "Too few arguments.\n");
		exit(1);
	}

	const char *workingDir = argv[1];
	const char *envvars = argv[2];
	const char *executable = argv[3];
	char **execArgs = &argv[4];

	changeWorkingDir(workingDir);
	setGivenEnvVars(envvars);
	dumpInformation();

	// Print a newline just in case whatever executed us printed data
	// without a newline. Otherwise the next process's "!> I have control"
	// command will not be properly recognized.
	// https://code.google.com/p/phusion-passenger/issues/detail?id=842#c16
	printf("\n");
	fflush(stdout);

	execvp(executable, (char * const *) execArgs);
	int e = errno;
	fprintf(stderr, "*** ERROR ***: Cannot execute %s: %s (%d)\n",
		executable, strerror(e), e);
	return 1;
}
<commit_msg>Set copyright header in SpawnPreparer.cpp<commit_after>/*
 *  Phusion Passenger - https://www.phusionpassenger.com/
 *  Copyright (c) 2012-2013 Phusion
 *
 *  "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
 *
 *  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.
 */

/*
 * Sets given environment variables, dumps the entire environment to
 * a given file (for diagnostics purposes), then execs the given command.
 *
 * This is a separate executable because it does quite
 * some non-async-signal-safe stuff that we can't do after
 * fork()ing from the Spawner and before exec()ing.
 */

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string>
#include <Utils/Base64.h>

using namespace std;
using namespace Passenger;

extern "C" {
	extern char **environ;
}

static void
changeWorkingDir(const char *dir) {
	int ret = chdir(dir);
	if (ret == 0) {
		setenv("PWD", dir, 1);
	} else {
		int e = errno;
		printf("!> Error\n");
		printf("!> \n");
		printf("Unable to change working directory to '%s': %s (errno=%d)\n",
			dir, strerror(e), e);
		fflush(stdout);
		exit(1);
	}
}

static void
setGivenEnvVars(const char *envvarsData) {
	string envvars = Base64::decode(envvarsData);
	const char *key = envvars.data();
	const char *end = envvars.data() + envvars.size();

	while (key < end) {
		const char *keyEnd = (const char *) memchr(key, '\0', end - key);
		if (keyEnd != NULL) {
			const char *value = keyEnd + 1;
			if (value < end) {
				const char *valueEnd = (const char *) memchr(value, '\0', end - value);
				if (valueEnd != NULL) {
					setenv(key, value, 1);
					key = valueEnd + 1;
				} else {
					break;
				}
			} else {
				break;
			}
		} else {
			break;
		}
	}
}

static void
dumpInformation() {
	const char *c_dir;
	if ((c_dir = getenv("PASSENGER_DEBUG_DIR")) == NULL) {
		return;
	}

	FILE *f;
	string dir = c_dir;

	f = fopen((dir + "/envvars").c_str(), "w");
	if (f != NULL) {
		int i = 0;
		while (environ[i] != NULL) {
			fputs(environ[i], f);
			putc('\n', f);
			i++;
		}
		fclose(f);
	}

	f = fopen((dir + "/user_info").c_str(), "w");
	if (f != NULL) {
		pid_t pid = fork();
		if (pid == 0) {
			dup2(fileno(f), 1);
			execlp("id", "id", (char *) 0);
			_exit(1);
		} else if (pid == -1) {
			int e = errno;
			fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
				strerror(e), e);
		} else {
			waitpid(pid, NULL, 0);
		}
		fclose(f);
	}

	f = fopen((dir + "/ulimit").c_str(), "w");
	if (f != NULL) {
		pid_t pid = fork();
		if (pid == 0) {
			dup2(fileno(f), 1);
			execlp("ulimit", "ulimit", "-a", (char *) 0);
			_exit(1);
		} else if (pid == -1) {
			int e = errno;
			fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
				strerror(e), e);
		} else {
			waitpid(pid, NULL, 0);
		}
		fclose(f);
	}

	f = fopen((dir + "/ulimit").c_str(), "w");
	if (f != NULL) {
		pid_t pid = fork();
		if (pid == 0) {
			dup2(fileno(f), 1);
			execlp("ulimit", "ulimit", "-a", (char *) 0);
			_exit(1);
		} else if (pid == -1) {
			int e = errno;
			fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
				strerror(e), e);
		} else {
			waitpid(pid, NULL, 0);
		}
		fclose(f);
	}

	#ifdef __linux__
		// TODO: call helper-scripts/system-memory-stats.py
		f = fopen((dir + "/sysmemory").c_str(), "w");
		if (f != NULL) {
			pid_t pid = fork();
			if (pid == 0) {
				dup2(fileno(f), 1);
				execlp("free", "free", "-m", (char *) 0);
				_exit(1);
			} else if (pid == -1) {
				int e = errno;
				fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
					strerror(e), e);
			} else {
				waitpid(pid, NULL, 0);
			}
			fclose(f);
		}
	#endif
}

// Usage: SpawnPreparer <working directory> <envvars> <executable> <exec args...>
int
main(int argc, char *argv[]) {
	if (argc < 5) {
		fprintf(stderr, "Too few arguments.\n");
		exit(1);
	}

	const char *workingDir = argv[1];
	const char *envvars = argv[2];
	const char *executable = argv[3];
	char **execArgs = &argv[4];

	changeWorkingDir(workingDir);
	setGivenEnvVars(envvars);
	dumpInformation();

	// Print a newline just in case whatever executed us printed data
	// without a newline. Otherwise the next process's "!> I have control"
	// command will not be properly recognized.
	// https://code.google.com/p/phusion-passenger/issues/detail?id=842#c16
	printf("\n");
	fflush(stdout);

	execvp(executable, (char * const *) execArgs);
	int e = errno;
	fprintf(stderr, "*** ERROR ***: Cannot execute %s: %s (%d)\n",
		executable, strerror(e), e);
	return 1;
}
<|endoftext|>


<commit_before>// This Source Code Form is licensed MPLv2: http://mozilla.org/MPL/2.0
#include "uithread.hh"
#include "internal.hh"
#include "../configure.h"
#include <semaphore.h>
#include <stdlib.h>
#include <deque>
#include <set>

namespace Rapicorn {

class ServerConnectionSource;
typedef std::shared_ptr<ServerConnectionSource> ServerConnectionSourceP;

static StaticUndeletable<Aida::BaseConnectionP*> global_server_connection; // automatically allocated and never destroyed

class ServerConnectionSource : public virtual EventSource {
  const char             *WHERE;
  PollFD                  pollfd_;
  bool                    last_seen_primary_, need_check_primary_;
  ServerConnectionSource (EventLoop &loop, Aida::BaseConnectionP connection) :
    WHERE ("Rapicorn::UIThread::ServerConnection"),
    last_seen_primary_ (false), need_check_primary_ (false)
  {
    assert (*global_server_connection == NULL);
    *global_server_connection = connection;
    assert (*global_server_connection != NULL);
    primary (false);
    pollfd_.fd = global_server_connection->get()->notify_fd();
    pollfd_.events = PollFD::IN;
    pollfd_.revents = 0;
  }
  friend class FriendAllocator<ServerConnectionSource>;
public:
  static ServerConnectionSourceP
  create (EventLoop &loop, Aida::BaseConnectionP connection)
  {
    ServerConnectionSourceP self = FriendAllocator<ServerConnectionSource>::make_shared (loop, connection);
    loop.add (self, EventLoop::PRIORITY_NORMAL);
    self->add_poll (&self->pollfd_);
    return self;
  }
private:
  ~ServerConnectionSource ()
  {
    remove_poll (&pollfd_);
    loop_remove();
  }
  virtual bool
  prepare (const LoopState &state, int64*)
  {
    if (UNLIKELY (last_seen_primary_ && !state.seen_primary))
      need_check_primary_ = true;
    return need_check_primary_ || global_server_connection->get()->pending();
  }
  virtual bool
  check (const LoopState &state)
  {
    if (UNLIKELY (last_seen_primary_ && !state.seen_primary))
      need_check_primary_ = true;
    last_seen_primary_ = state.seen_primary;
    return need_check_primary_ || global_server_connection->get()->pending();
  }
  virtual bool
  dispatch (const LoopState &state)
  {
    global_server_connection->get()->dispatch();
    if (need_check_primary_)
      {
        need_check_primary_ = false;
        loop_->exec_idle (check_primaries);
      }
    return true;
  }
  static void
  check_primaries()
  {
    // seen_primary is merely a hint, this handler checks the real loop state
    const bool uithread_finishable = uithread_main_loop()->finishable();
    if (uithread_finishable)
      ApplicationImpl::the().lost_primaries();
  }
};

struct Initializer {
  int *argcp; char **argv; const StringVector *args;
  Mutex mutex; Cond cond; bool done;
};

static Atomic<ThreadInfo*> uithread_threadinfo = NULL;

class UIThread {
  std::thread             thread_;
  pthread_mutex_t         thread_mutex_;
  volatile bool           running_;
  Initializer            *idata_;
  const MainLoopP         main_loop_;
public:
  UIThread (Initializer *idata) :
    thread_mutex_ (PTHREAD_MUTEX_INITIALIZER), running_ (0), idata_ (idata),
    main_loop_ (MainLoop::create())
  {}
  bool  running() const { return running_; }
  void
  start()
  {
    pthread_mutex_lock (&thread_mutex_);
    if (thread_.get_id() == std::thread::id())
      {
        assert (running_ == false);
        thread_ = std::thread (std::ref (*this));
      }
    pthread_mutex_unlock (&thread_mutex_);
  }
  void
  join()
  {
    pthread_mutex_lock (&thread_mutex_);
    if (thread_.joinable())
      thread_.join();
    pthread_mutex_unlock (&thread_mutex_);
    assert (running_ == false);
  }
  void
  queue_stop()
  {
    pthread_mutex_lock (&thread_mutex_);
    main_loop_->quit();
    pthread_mutex_unlock (&thread_mutex_);
  }
  MainLoopP         main_loop()   { return main_loop_; }
private:
  ~UIThread ()
  {
    fatal ("UIThread singleton in dtor");
    // FIXME: leaking ServerConnectionSource ref count...
  }
  void
  initialize ()
  {
    assert_return (idata_ != NULL);
    // idata_core() already called
    ThisThread::affinity (string_to_int (string_vector_find_value (*idata_->args, "cpu-affinity=", "-1")));
    // initialize Application singleton
    ApplicationImpl &application = ApplicationImpl::the();
    // setup Aida server connection for RPC calls
    Aida::BaseConnectionP connection =
      Aida::ServerConnection::bind<ApplicationIface> ("inproc://Rapicorn-" RAPICORN_VERSION,
                                                      shared_ptr_cast<ApplicationIface> (&application));
    // initialize sub systems
    struct InitHookCaller : public InitHook {
      static void  invoke (const String &kind, int *argcp, char **argv, const StringVector &args)
      { invoke_hooks (kind, argcp, argv, args); }
    };
    // UI library core parts
    InitHookCaller::invoke ("ui-core/", idata_->argcp, idata_->argv, *idata_->args);
    // Application Singleton
    InitHookCaller::invoke ("ui-thread/", idata_->argcp, idata_->argv, *idata_->args);
    assert_return (NULL != &ApplicationImpl::the());
    // Initializations after Application Singleton
    InitHookCaller::invoke ("ui-app/", idata_->argcp, idata_->argv, *idata_->args);
    // hook up server connection to main loop to process remote calls
    ServerConnectionSourceP server_source = ServerConnectionSource::create (*main_loop_, connection);
    // Complete initialization by signalling caller
    idata_->done = true;
    idata_->mutex.lock();
    idata_->cond.signal();
    idata_->mutex.unlock();
    idata_ = NULL;
  }
public:
  void
  operator() ()
  {
    assert (uithread_threadinfo == NULL);
    uithread_threadinfo = &ThreadInfo::self();
    ThreadInfo::self().name ("RapicornUIThread");
    const bool running_twice = __sync_fetch_and_add (&running_, +1);
    assert (running_twice == false);

    initialize();
    assert_return (idata_ == NULL);
    main_loop_->run();
    WindowImpl::forcefully_close_all();
    DisplayDriver::forcefully_close_all();
    while (!main_loop_->finishable())
      if (!main_loop_->iterate (false))
        break;  // handle pending idle handlers like exec_*()
    main_loop_->destroy_loop();

    assert (running_ == true);
    const bool stopped_twice = !__sync_fetch_and_sub (&running_, +1);
    assert (stopped_twice == false);

    assert (running_ == false);
    assert (uithread_threadinfo == &ThreadInfo::self());
    uithread_threadinfo = NULL;
  }
};
static UIThread *the_uithread = NULL;

MainLoopP
uithread_main_loop ()
{
  return the_uithread ? the_uithread->main_loop() : NULL;
}

bool
uithread_is_current ()
{
  return uithread_threadinfo == &ThreadInfo::self();
}

void
uithread_shutdown (void)
{
  if (the_uithread && the_uithread->running())
    {
      the_uithread->queue_stop(); // stops ui thread main loop
      the_uithread->join();
    }
}

static void
uithread_uncancelled_atexit()
{
  if (the_uithread && the_uithread->running())
    {
      /* For proper shutdown, the ui-thread needs to stop running before global
       * dtors or any atexit() handlers are being executed. C9x and C++03 leave
       * this unsolved, so we provide explicit API for the user, like
       * Rapicorn::exit() and Application::shutdown(). In case these are omitted,
       * we're not 100% safe, some earlier atexit() handler could have shot some
       * of our required resources already, however we do our best to start a
       * graceful shutdown at this point.
       */
      critical ("FIX""ME: UI-Thread still running during exit(), call Application::shutdown()");
      uithread_shutdown();
    }
}

static void wrap_test_runner (void);

bool // provide errno on error
uithread_bootup (int *argcp, char **argv, const StringVector &args) // internal.hh
{
  assert_return (the_uithread == NULL, false);
  // catch exit() while UIThread is still running
  atexit (uithread_uncancelled_atexit);
  // setup client/server connection pair
  Initializer idata;
  // initialize and create UIThread
  idata.argcp = argcp; idata.argv = argv; idata.args = &args; idata.done = false;
  // start and syncronize with thread
  idata.mutex.lock();
  the_uithread = new UIThread (&idata);
  the_uithread->start();
  while (!idata.done)
    idata.cond.wait (idata.mutex);
  idata.mutex.unlock();
  assert (the_uithread->running());
  // install handler for UIThread test cases
  wrap_test_runner();
  errno = 0;
  return true;
}

} // Rapicorn

#include <rcore/testutils.hh>

namespace Rapicorn {

// === UI-Thread Syscalls ===
struct Callable {
  virtual int64 operator() () = 0;
  virtual      ~Callable   () {}
};
static std::deque<Callable*> syscall_queue;
static Mutex                 syscall_mutex;

static int64
ui_thread_syscall (Callable *callable)
{
  syscall_mutex.lock();
  syscall_queue.push_back (callable);
  syscall_mutex.unlock();
  const int64 result = client_app_test_hook();
  return result;
}

int64
server_app_test_hook()
{
  int64 result = -1;
  syscall_mutex.lock();
  while (syscall_queue.size())
    {
      Callable &callable = *syscall_queue.front();
      syscall_queue.pop_front();
      syscall_mutex.unlock();
      result = callable ();
      delete &callable;
      syscall_mutex.lock();
    }
  syscall_mutex.unlock();
  return result;
}

// === UI-Thread Test Runs ===
class SyscallTestRunner : public Callable {
  void (*test_runner) (void);
public:
  SyscallTestRunner (void (*func) (void)) : test_runner (func) {}
  int64 operator()  ()                    { test_runner(); return 0; }
};

static void
wrap_test_runner (void)
{
  Test::RegisterTest::test_set_trigger (uithread_test_trigger);
}

// === UI-Thread Test Trigger ===
class SyscallTestTrigger : public Callable {
  void (*test_func) (void);
public:
  SyscallTestTrigger (void (*tfunc) (void)) : test_func (tfunc) {}
  int64 operator()  ()                      { test_func(); return 0; }
};

void
uithread_test_trigger (void (*test_func) ())
{
  assert_return (test_func != NULL);
  assert_return (the_uithread != NULL);
  // run tests from ui-thread
  ui_thread_syscall (new SyscallTestTrigger (test_func));
  // ensure ui-thread shutdown
  uithread_shutdown();
}

} // Rapicorn
<commit_msg>UI: uithread: initialize and use uithread_threadinfo without Atomic<><commit_after>// This Source Code Form is licensed MPLv2: http://mozilla.org/MPL/2.0
#include "uithread.hh"
#include "internal.hh"
#include "../configure.h"
#include <semaphore.h>
#include <stdlib.h>
#include <deque>
#include <set>

namespace Rapicorn {

class ServerConnectionSource;
typedef std::shared_ptr<ServerConnectionSource> ServerConnectionSourceP;

static StaticUndeletable<Aida::BaseConnectionP*> global_server_connection; // automatically allocated and never destroyed

class ServerConnectionSource : public virtual EventSource {
  const char             *WHERE;
  PollFD                  pollfd_;
  bool                    last_seen_primary_, need_check_primary_;
  ServerConnectionSource (EventLoop &loop, Aida::BaseConnectionP connection) :
    WHERE ("Rapicorn::UIThread::ServerConnection"),
    last_seen_primary_ (false), need_check_primary_ (false)
  {
    assert (*global_server_connection == NULL);
    *global_server_connection = connection;
    assert (*global_server_connection != NULL);
    primary (false);
    pollfd_.fd = global_server_connection->get()->notify_fd();
    pollfd_.events = PollFD::IN;
    pollfd_.revents = 0;
  }
  friend class FriendAllocator<ServerConnectionSource>;
public:
  static ServerConnectionSourceP
  create (EventLoop &loop, Aida::BaseConnectionP connection)
  {
    ServerConnectionSourceP self = FriendAllocator<ServerConnectionSource>::make_shared (loop, connection);
    loop.add (self, EventLoop::PRIORITY_NORMAL);
    self->add_poll (&self->pollfd_);
    return self;
  }
private:
  ~ServerConnectionSource ()
  {
    remove_poll (&pollfd_);
    loop_remove();
  }
  virtual bool
  prepare (const LoopState &state, int64*)
  {
    if (UNLIKELY (last_seen_primary_ && !state.seen_primary))
      need_check_primary_ = true;
    return need_check_primary_ || global_server_connection->get()->pending();
  }
  virtual bool
  check (const LoopState &state)
  {
    if (UNLIKELY (last_seen_primary_ && !state.seen_primary))
      need_check_primary_ = true;
    last_seen_primary_ = state.seen_primary;
    return need_check_primary_ || global_server_connection->get()->pending();
  }
  virtual bool
  dispatch (const LoopState &state)
  {
    global_server_connection->get()->dispatch();
    if (need_check_primary_)
      {
        need_check_primary_ = false;
        loop_->exec_idle (check_primaries);
      }
    return true;
  }
  static void
  check_primaries()
  {
    // seen_primary is merely a hint, this handler checks the real loop state
    const bool uithread_finishable = uithread_main_loop()->finishable();
    if (uithread_finishable)
      ApplicationImpl::the().lost_primaries();
  }
};

struct Initializer {
  int *argcp; char **argv; const StringVector *args;
  Mutex mutex; Cond cond; bool done;
};

static ThreadInfo *volatile uithread_threadinfo = NULL;

class UIThread {
  std::thread             thread_;
  pthread_mutex_t         thread_mutex_;
  volatile bool           running_;
  Initializer            *idata_;
  const MainLoopP         main_loop_;
public:
  UIThread (Initializer *idata) :
    thread_mutex_ (PTHREAD_MUTEX_INITIALIZER), running_ (0), idata_ (idata),
    main_loop_ (MainLoop::create())
  {}
  bool  running() const { return running_; }
  void
  start()
  {
    pthread_mutex_lock (&thread_mutex_);
    if (thread_.get_id() == std::thread::id())
      {
        assert (running_ == false);
        thread_ = std::thread (std::ref (*this));
      }
    pthread_mutex_unlock (&thread_mutex_);
  }
  void
  join()
  {
    pthread_mutex_lock (&thread_mutex_);
    if (thread_.joinable())
      thread_.join();
    pthread_mutex_unlock (&thread_mutex_);
    assert (running_ == false);
  }
  void
  queue_stop()
  {
    pthread_mutex_lock (&thread_mutex_);
    main_loop_->quit();
    pthread_mutex_unlock (&thread_mutex_);
  }
  MainLoopP         main_loop()   { return main_loop_; }
private:
  ~UIThread ()
  {
    fatal ("UIThread singleton in dtor");
    // FIXME: leaking ServerConnectionSource ref count...
  }
  void
  initialize ()
  {
    assert_return (idata_ != NULL);
    // idata_core() already called
    ThisThread::affinity (string_to_int (string_vector_find_value (*idata_->args, "cpu-affinity=", "-1")));
    // initialize Application singleton
    ApplicationImpl &application = ApplicationImpl::the();
    // setup Aida server connection for RPC calls
    Aida::BaseConnectionP connection =
      Aida::ServerConnection::bind<ApplicationIface> ("inproc://Rapicorn-" RAPICORN_VERSION,
                                                      shared_ptr_cast<ApplicationIface> (&application));
    // initialize sub systems
    struct InitHookCaller : public InitHook {
      static void  invoke (const String &kind, int *argcp, char **argv, const StringVector &args)
      { invoke_hooks (kind, argcp, argv, args); }
    };
    // UI library core parts
    InitHookCaller::invoke ("ui-core/", idata_->argcp, idata_->argv, *idata_->args);
    // Application Singleton
    InitHookCaller::invoke ("ui-thread/", idata_->argcp, idata_->argv, *idata_->args);
    assert_return (NULL != &ApplicationImpl::the());
    // Initializations after Application Singleton
    InitHookCaller::invoke ("ui-app/", idata_->argcp, idata_->argv, *idata_->args);
    // hook up server connection to main loop to process remote calls
    ServerConnectionSourceP server_source = ServerConnectionSource::create (*main_loop_, connection);
    // Complete initialization by signalling caller
    idata_->done = true;
    idata_->mutex.lock();
    idata_->cond.signal();
    idata_->mutex.unlock();
    idata_ = NULL;
  }
public:
  void
  operator() ()
  {
    assert (atomic_load (&uithread_threadinfo) == NULL);
    atomic_store (&uithread_threadinfo, &ThreadInfo::self());
    ThreadInfo::self().name ("RapicornUIThread");
    const bool running_twice = __sync_fetch_and_add (&running_, +1);
    assert (running_twice == false);

    initialize();
    assert_return (idata_ == NULL);
    main_loop_->run();
    WindowImpl::forcefully_close_all();
    DisplayDriver::forcefully_close_all();
    while (!main_loop_->finishable())
      if (!main_loop_->iterate (false))
        break;  // handle pending idle handlers like exec_*()
    main_loop_->destroy_loop();

    assert (running_ == true);
    const bool stopped_twice = !__sync_fetch_and_sub (&running_, +1);
    assert (stopped_twice == false);

    assert (running_ == false);
    ThreadInfo *last_threadinfo = atomic_swap (&uithread_threadinfo, (ThreadInfo*) NULL);
    assert (last_threadinfo == &ThreadInfo::self());
  }
};
static UIThread *the_uithread = NULL;

MainLoopP
uithread_main_loop ()
{
  return the_uithread ? the_uithread->main_loop() : NULL;
}

bool
uithread_is_current ()
{
  return atomic_load (&uithread_threadinfo) == &ThreadInfo::self();
}

void
uithread_shutdown (void)
{
  if (the_uithread && the_uithread->running())
    {
      the_uithread->queue_stop(); // stops ui thread main loop
      the_uithread->join();
    }
}

static void
uithread_uncancelled_atexit()
{
  if (the_uithread && the_uithread->running())
    {
      /* For proper shutdown, the ui-thread needs to stop running before global
       * dtors or any atexit() handlers are being executed. C9x and C++03 leave
       * this unsolved, so we provide explicit API for the user, like
       * Rapicorn::exit() and Application::shutdown(). In case these are omitted,
       * we're not 100% safe, some earlier atexit() handler could have shot some
       * of our required resources already, however we do our best to start a
       * graceful shutdown at this point.
       */
      critical ("FIX""ME: UI-Thread still running during exit(), call Application::shutdown()");
      uithread_shutdown();
    }
}

static void wrap_test_runner (void);

bool // provide errno on error
uithread_bootup (int *argcp, char **argv, const StringVector &args) // internal.hh
{
  assert_return (the_uithread == NULL, false);
  // catch exit() while UIThread is still running
  atexit (uithread_uncancelled_atexit);
  // setup client/server connection pair
  Initializer idata;
  // initialize and create UIThread
  idata.argcp = argcp; idata.argv = argv; idata.args = &args; idata.done = false;
  // start and syncronize with thread
  idata.mutex.lock();
  the_uithread = new UIThread (&idata);
  the_uithread->start();
  while (!idata.done)
    idata.cond.wait (idata.mutex);
  idata.mutex.unlock();
  assert (the_uithread->running());
  // install handler for UIThread test cases
  wrap_test_runner();
  errno = 0;
  return true;
}

} // Rapicorn

#include <rcore/testutils.hh>

namespace Rapicorn {

// === UI-Thread Syscalls ===
struct Callable {
  virtual int64 operator() () = 0;
  virtual      ~Callable   () {}
};
static std::deque<Callable*> syscall_queue;
static Mutex                 syscall_mutex;

static int64
ui_thread_syscall (Callable *callable)
{
  syscall_mutex.lock();
  syscall_queue.push_back (callable);
  syscall_mutex.unlock();
  const int64 result = client_app_test_hook();
  return result;
}

int64
server_app_test_hook()
{
  int64 result = -1;
  syscall_mutex.lock();
  while (syscall_queue.size())
    {
      Callable &callable = *syscall_queue.front();
      syscall_queue.pop_front();
      syscall_mutex.unlock();
      result = callable ();
      delete &callable;
      syscall_mutex.lock();
    }
  syscall_mutex.unlock();
  return result;
}

// === UI-Thread Test Runs ===
class SyscallTestRunner : public Callable {
  void (*test_runner) (void);
public:
  SyscallTestRunner (void (*func) (void)) : test_runner (func) {}
  int64 operator()  ()                    { test_runner(); return 0; }
};

static void
wrap_test_runner (void)
{
  Test::RegisterTest::test_set_trigger (uithread_test_trigger);
}

// === UI-Thread Test Trigger ===
class SyscallTestTrigger : public Callable {
  void (*test_func) (void);
public:
  SyscallTestTrigger (void (*tfunc) (void)) : test_func (tfunc) {}
  int64 operator()  ()                      { test_func(); return 0; }
};

void
uithread_test_trigger (void (*test_func) ())
{
  assert_return (test_func != NULL);
  assert_return (the_uithread != NULL);
  // run tests from ui-thread
  ui_thread_syscall (new SyscallTestTrigger (test_func));
  // ensure ui-thread shutdown
  uithread_shutdown();
}

} // Rapicorn
<|endoftext|>