<commit_before>//===--- DWARFVisitor.cpp ---------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//

#include <unordered_map>

#include "DWARFVisitor.h"
#include "llvm/ObjectYAML/DWARFYAML.h"

using namespace llvm;

template <typename T>
void DWARFYAML::VisitorImpl<T>::onVariableSizeValue(uint64_t U, unsigned Size) {
  switch (Size) {
  case 8:
    onValue((uint64_t)U);
    break;
  case 4:
    onValue((uint32_t)U);
    break;
  case 2:
    onValue((uint16_t)U);
    break;
  case 1:
    onValue((uint8_t)U);
    break;
  default:
    llvm_unreachable("Invalid integer write size.");
  }
}

static unsigned getOffsetSize(const DWARFYAML::Unit &Unit) {
  return Unit.Length.isDWARF64() ? 8 : 4;
}

static unsigned getRefSize(const DWARFYAML::Unit &Unit) {
  if (Unit.Version == 2)
    return Unit.AddrSize;
  return getOffsetSize(Unit);
}

template <typename T> void DWARFYAML::VisitorImpl<T>::traverseDebugInfo() {
  // XXX BINARYEN: Handle multiple linked compile units, each of which can
  // refer to a different abbreviation list.
  // TODO: This code appears to assume that abbreviation codes increment by 1
  // so that lookups are linear. In LLVM output that is true, but it might not
  // be in general.
  // Create a map of [byte offset into the abbreviation section] => [index in
  // DebugInfo.AbbrevDecls]. This avoids linear search for each CU.
  std::unordered_map<size_t, size_t> abbrByteOffsetToDeclsIndex;
  for (size_t i = 0; i < DebugInfo.AbbrevDecls.size(); i++) {
    auto offset = DebugInfo.AbbrevDecls[i].ListOffset;
    // The offset is the same for all entries for the same CU, so only note the
    // first as that is where the list for the CU (that LLVM DeclSet) begins.
    // That is, DebugInfo.AbbrevDecls looks like this:
    //
    //  i  CU   Abbrev  ListOffset
    // ============================
    //  0   X     X1      150
    //  1   X     X2      150
    //  2   X     X3      150
    //           ..
    //  6   Y     Y1      260
    //  7   Y     Y2      260
    //
    // Note how multiple rows i have the same CU. All those abbrevs have the
    // same ListOffset, which is the byte offset into the abbreviation section
    // for that set of abbreviations.
    if (abbrByteOffsetToDeclsIndex.count(offset)) {
      continue;
    }
    abbrByteOffsetToDeclsIndex[offset] = i;
  }
  for (auto &Unit : DebugInfo.CompileUnits) {
    // AbbrOffset is the byte offset into the abbreviation section, which we
    // need to find among the Abbrev's ListOffsets (which are the byte offsets
    // of where that abbreviation list begins).
    // TODO: Optimize this to not be O(#CUs * #abbrevs).
    auto offset = Unit.AbbrOffset;
    assert(abbrByteOffsetToDeclsIndex.count(offset));
    size_t AbbrevStart = abbrByteOffsetToDeclsIndex[offset];
    assert(DebugInfo.AbbrevDecls[AbbrevStart].ListOffset == offset);
    // Find the last entry in this abbreviation list.
    size_t AbbrevEnd = AbbrevStart;
    while (AbbrevEnd < DebugInfo.AbbrevDecls.size() &&
           DebugInfo.AbbrevDecls[AbbrevEnd].Code) {
      AbbrevEnd++;
    }
    // XXX BINARYEN If there are no abbreviations, there is nothing to
    //              do in this unit.
    if (AbbrevStart == AbbrevEnd) {
      continue;
    }
    onStartCompileUnit(Unit);
    if (Unit.Entries.empty()) { // XXX BINARYEN
      continue;
    }
    for (auto &Entry : Unit.Entries) {
      onStartDIE(Unit, Entry);
      if (Entry.AbbrCode == 0u)
        continue;
      // XXX BINARYEN valid abbreviation codes start from 1, so subtract that,
      //              and are relative to the start of the abbrev table
      auto RelativeAbbrIndex = Entry.AbbrCode - 1 + AbbrevStart;
      if (RelativeAbbrIndex >= AbbrevEnd) {
        errs() << "warning: invalid abbreviation code " << Entry.AbbrCode
               << " (range: " << AbbrevStart << ".." << AbbrevEnd << ")\n";
        continue;
      }
      auto &Abbrev = DebugInfo.AbbrevDecls[RelativeAbbrIndex];
      auto FormVal = Entry.Values.begin();
      auto AbbrForm = Abbrev.Attributes.begin();
      for (;
           FormVal != Entry.Values.end() && AbbrForm != Abbrev.Attributes.end();
           ++FormVal, ++AbbrForm) {
        onForm(*AbbrForm, *FormVal);
        dwarf::Form Form = AbbrForm->Form;
        bool Indirect;
        do {
          Indirect = false;
          switch (Form) {
          case dwarf::DW_FORM_addr:
            onVariableSizeValue(FormVal->Value, Unit.AddrSize);
            break;
          case dwarf::DW_FORM_ref_addr:
            onVariableSizeValue(FormVal->Value, getRefSize(Unit));
            break;
          case dwarf::DW_FORM_exprloc:
          case dwarf::DW_FORM_block:
            onValue((uint64_t)FormVal->BlockData.size(), true);
            onValue(
                MemoryBufferRef(StringRef((const char *)&FormVal->BlockData[0],
                                          FormVal->BlockData.size()),
                                ""));
            break;
          case dwarf::DW_FORM_block1: {
            auto writeSize = FormVal->BlockData.size();
            onValue((uint8_t)writeSize);
            onValue(
                MemoryBufferRef(StringRef((const char *)&FormVal->BlockData[0],
                                          FormVal->BlockData.size()),
                                ""));
            break;
          }
          case dwarf::DW_FORM_block2: {
            auto writeSize = FormVal->BlockData.size();
            onValue((uint16_t)writeSize);
            onValue(
                MemoryBufferRef(StringRef((const char *)&FormVal->BlockData[0],
                                          FormVal->BlockData.size()),
                                ""));
            break;
          }
          case dwarf::DW_FORM_block4: {
            auto writeSize = FormVal->BlockData.size();
            onValue((uint32_t)writeSize);
            onValue(
                MemoryBufferRef(StringRef((const char *)&FormVal->BlockData[0],
                                          FormVal->BlockData.size()),
                                ""));
            break;
          }
          case dwarf::DW_FORM_data1:
          case dwarf::DW_FORM_ref1:
          case dwarf::DW_FORM_flag:
          case dwarf::DW_FORM_strx1:
          case dwarf::DW_FORM_addrx1:
            onValue((uint8_t)FormVal->Value);
            break;
          case dwarf::DW_FORM_data2:
          case dwarf::DW_FORM_ref2:
          case dwarf::DW_FORM_strx2:
          case dwarf::DW_FORM_addrx2:
            onValue((uint16_t)FormVal->Value);
            break;
          case dwarf::DW_FORM_data4:
          case dwarf::DW_FORM_ref4:
          case dwarf::DW_FORM_ref_sup4:
          case dwarf::DW_FORM_strx4:
          case dwarf::DW_FORM_addrx4:
            onValue((uint32_t)FormVal->Value);
            break;
          case dwarf::DW_FORM_data8:
          case dwarf::DW_FORM_ref8:
          case dwarf::DW_FORM_ref_sup8:
            onValue((uint64_t)FormVal->Value);
            break;
          case dwarf::DW_FORM_sdata:
            onValue((int64_t)FormVal->Value, true);
            break;
          case dwarf::DW_FORM_udata:
          case dwarf::DW_FORM_ref_udata:
            onValue((uint64_t)FormVal->Value, true);
            break;
          case dwarf::DW_FORM_string:
            onValue(FormVal->CStr);
            break;
          case dwarf::DW_FORM_indirect:
            onValue((uint64_t)FormVal->Value, true);
            Indirect = true;
            Form = static_cast<dwarf::Form>((uint64_t)FormVal->Value);
            ++FormVal;
            break;
          case dwarf::DW_FORM_strp:
          case dwarf::DW_FORM_sec_offset:
          case dwarf::DW_FORM_GNU_ref_alt:
          case dwarf::DW_FORM_GNU_strp_alt:
          case dwarf::DW_FORM_line_strp:
          case dwarf::DW_FORM_strp_sup:
            onVariableSizeValue(FormVal->Value, getOffsetSize(Unit));
            break;
          case dwarf::DW_FORM_ref_sig8:
            onValue((uint64_t)FormVal->Value);
            break;
          case dwarf::DW_FORM_GNU_addr_index:
          case dwarf::DW_FORM_GNU_str_index:
            onValue((uint64_t)FormVal->Value, true);
            break;
          default:
            break;
          }
        } while (Indirect);
      }
      onEndDIE(Unit, Entry);
    }
    onEndCompileUnit(Unit);
  }
}

// Explicitly instantiate the two template expansions.
template class DWARFYAML::VisitorImpl<DWARFYAML::Data>;
template class DWARFYAML::VisitorImpl<const DWARFYAML::Data>;
<commit_msg>Fix assert on access of empty vector (#4045)<commit_after>//===--- DWARFVisitor.cpp ---------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//

#include <unordered_map>

#include "DWARFVisitor.h"
#include "llvm/ObjectYAML/DWARFYAML.h"

using namespace llvm;

template <typename T>
void DWARFYAML::VisitorImpl<T>::onVariableSizeValue(uint64_t U, unsigned Size) {
  switch (Size) {
  case 8:
    onValue((uint64_t)U);
    break;
  case 4:
    onValue((uint32_t)U);
    break;
  case 2:
    onValue((uint16_t)U);
    break;
  case 1:
    onValue((uint8_t)U);
    break;
  default:
    llvm_unreachable("Invalid integer write size.");
  }
}

static unsigned getOffsetSize(const DWARFYAML::Unit &Unit) {
  return Unit.Length.isDWARF64() ? 8 : 4;
}

static unsigned getRefSize(const DWARFYAML::Unit &Unit) {
  if (Unit.Version == 2)
    return Unit.AddrSize;
  return getOffsetSize(Unit);
}

template <typename T> void DWARFYAML::VisitorImpl<T>::traverseDebugInfo() {
  // XXX BINARYEN: Handle multiple linked compile units, each of which can
  // refer to a different abbreviation list.
  // TODO: This code appears to assume that abbreviation codes increment by 1
  // so that lookups are linear. In LLVM output that is true, but it might not
  // be in general.
  // Create a map of [byte offset into the abbreviation section] => [index in
  // DebugInfo.AbbrevDecls]. This avoids linear search for each CU.
  std::unordered_map<size_t, size_t> abbrByteOffsetToDeclsIndex;
  for (size_t i = 0; i < DebugInfo.AbbrevDecls.size(); i++) {
    auto offset = DebugInfo.AbbrevDecls[i].ListOffset;
    // The offset is the same for all entries for the same CU, so only note the
    // first as that is where the list for the CU (that LLVM DeclSet) begins.
    // That is, DebugInfo.AbbrevDecls looks like this:
    //
    //  i  CU   Abbrev  ListOffset
    // ============================
    //  0   X     X1      150
    //  1   X     X2      150
    //  2   X     X3      150
    //           ..
    //  6   Y     Y1      260
    //  7   Y     Y2      260
    //
    // Note how multiple rows i have the same CU. All those abbrevs have the
    // same ListOffset, which is the byte offset into the abbreviation section
    // for that set of abbreviations.
    if (abbrByteOffsetToDeclsIndex.count(offset)) {
      continue;
    }
    abbrByteOffsetToDeclsIndex[offset] = i;
  }
  for (auto &Unit : DebugInfo.CompileUnits) {
    // AbbrOffset is the byte offset into the abbreviation section, which we
    // need to find among the Abbrev's ListOffsets (which are the byte offsets
    // of where that abbreviation list begins).
    // TODO: Optimize this to not be O(#CUs * #abbrevs).
    auto offset = Unit.AbbrOffset;
    assert(abbrByteOffsetToDeclsIndex.count(offset));
    size_t AbbrevStart = abbrByteOffsetToDeclsIndex[offset];
    assert(DebugInfo.AbbrevDecls[AbbrevStart].ListOffset == offset);
    // Find the last entry in this abbreviation list.
    size_t AbbrevEnd = AbbrevStart;
    while (AbbrevEnd < DebugInfo.AbbrevDecls.size() &&
           DebugInfo.AbbrevDecls[AbbrevEnd].Code) {
      AbbrevEnd++;
    }
    // XXX BINARYEN If there are no abbreviations, there is nothing to
    //              do in this unit.
    if (AbbrevStart == AbbrevEnd) {
      continue;
    }
    onStartCompileUnit(Unit);
    if (Unit.Entries.empty()) { // XXX BINARYEN
      continue;
    }
    for (auto &Entry : Unit.Entries) {
      onStartDIE(Unit, Entry);
      if (Entry.AbbrCode == 0u)
        continue;
      // XXX BINARYEN valid abbreviation codes start from 1, so subtract that,
      //              and are relative to the start of the abbrev table
      auto RelativeAbbrIndex = Entry.AbbrCode - 1 + AbbrevStart;
      if (RelativeAbbrIndex >= AbbrevEnd) {
        errs() << "warning: invalid abbreviation code " << Entry.AbbrCode
               << " (range: " << AbbrevStart << ".." << AbbrevEnd << ")\n";
        continue;
      }
      auto &Abbrev = DebugInfo.AbbrevDecls[RelativeAbbrIndex];
      auto FormVal = Entry.Values.begin();
      auto AbbrForm = Abbrev.Attributes.begin();
      for (;
           FormVal != Entry.Values.end() && AbbrForm != Abbrev.Attributes.end();
           ++FormVal, ++AbbrForm) {
        onForm(*AbbrForm, *FormVal);
        dwarf::Form Form = AbbrForm->Form;
        bool Indirect;
        do {
          Indirect = false;
          switch (Form) {
          case dwarf::DW_FORM_addr:
            onVariableSizeValue(FormVal->Value, Unit.AddrSize);
            break;
          case dwarf::DW_FORM_ref_addr:
            onVariableSizeValue(FormVal->Value, getRefSize(Unit));
            break;
          case dwarf::DW_FORM_exprloc:
          case dwarf::DW_FORM_block:
            onValue((uint64_t)FormVal->BlockData.size(), true);
            onValue(
                MemoryBufferRef(StringRef((const char *)FormVal->BlockData.data(),
                                          FormVal->BlockData.size()),
                                ""));
            break;
          case dwarf::DW_FORM_block1: {
            auto writeSize = FormVal->BlockData.size();
            onValue((uint8_t)writeSize);
            onValue(
                MemoryBufferRef(StringRef((const char *)FormVal->BlockData.data(),
                                          FormVal->BlockData.size()),
                                ""));
            break;
          }
          case dwarf::DW_FORM_block2: {
            auto writeSize = FormVal->BlockData.size();
            onValue((uint16_t)writeSize);
            onValue(
                MemoryBufferRef(StringRef((const char *)FormVal->BlockData.data(),
                                          FormVal->BlockData.size()),
                                ""));
            break;
          }
          case dwarf::DW_FORM_block4: {
            auto writeSize = FormVal->BlockData.size();
            onValue((uint32_t)writeSize);
            onValue(
                MemoryBufferRef(StringRef((const char *)FormVal->BlockData.data(),
                                          FormVal->BlockData.size()),
                                ""));
            break;
          }
          case dwarf::DW_FORM_data1:
          case dwarf::DW_FORM_ref1:
          case dwarf::DW_FORM_flag:
          case dwarf::DW_FORM_strx1:
          case dwarf::DW_FORM_addrx1:
            onValue((uint8_t)FormVal->Value);
            break;
          case dwarf::DW_FORM_data2:
          case dwarf::DW_FORM_ref2:
          case dwarf::DW_FORM_strx2:
          case dwarf::DW_FORM_addrx2:
            onValue((uint16_t)FormVal->Value);
            break;
          case dwarf::DW_FORM_data4:
          case dwarf::DW_FORM_ref4:
          case dwarf::DW_FORM_ref_sup4:
          case dwarf::DW_FORM_strx4:
          case dwarf::DW_FORM_addrx4:
            onValue((uint32_t)FormVal->Value);
            break;
          case dwarf::DW_FORM_data8:
          case dwarf::DW_FORM_ref8:
          case dwarf::DW_FORM_ref_sup8:
            onValue((uint64_t)FormVal->Value);
            break;
          case dwarf::DW_FORM_sdata:
            onValue((int64_t)FormVal->Value, true);
            break;
          case dwarf::DW_FORM_udata:
          case dwarf::DW_FORM_ref_udata:
            onValue((uint64_t)FormVal->Value, true);
            break;
          case dwarf::DW_FORM_string:
            onValue(FormVal->CStr);
            break;
          case dwarf::DW_FORM_indirect:
            onValue((uint64_t)FormVal->Value, true);
            Indirect = true;
            Form = static_cast<dwarf::Form>((uint64_t)FormVal->Value);
            ++FormVal;
            break;
          case dwarf::DW_FORM_strp:
          case dwarf::DW_FORM_sec_offset:
          case dwarf::DW_FORM_GNU_ref_alt:
          case dwarf::DW_FORM_GNU_strp_alt:
          case dwarf::DW_FORM_line_strp:
          case dwarf::DW_FORM_strp_sup:
            onVariableSizeValue(FormVal->Value, getOffsetSize(Unit));
            break;
          case dwarf::DW_FORM_ref_sig8:
            onValue((uint64_t)FormVal->Value);
            break;
          case dwarf::DW_FORM_GNU_addr_index:
          case dwarf::DW_FORM_GNU_str_index:
            onValue((uint64_t)FormVal->Value, true);
            break;
          default:
            break;
          }
        } while (Indirect);
      }
      onEndDIE(Unit, Entry);
    }
    onEndCompileUnit(Unit);
  }
}

// Explicitly instantiate the two template expansions.
template class DWARFYAML::VisitorImpl<DWARFYAML::Data>;
template class DWARFYAML::VisitorImpl<const DWARFYAML::Data>;
<|endoftext|>


<commit_before>#include "sqlite.hh"
#include "util.hh"

#include <sqlite3.h>

namespace nix {

[[noreturn]] void throwSQLiteError(sqlite3 * db, const format & f)
{
    int err = sqlite3_errcode(db);
    if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) {
        if (err == SQLITE_PROTOCOL)
            printError("warning: SQLite database is busy (SQLITE_PROTOCOL)");
        else {
            static bool warned = false;
            if (!warned) {
                printError("warning: SQLite database is busy");
                warned = true;
            }
        }
        /* Sleep for a while since retrying the transaction right away
           is likely to fail again. */
        checkInterrupt();
#if HAVE_NANOSLEEP
        struct timespec t;
        t.tv_sec = 0;
        t.tv_nsec = (random() % 100) * 1000 * 1000; /* <= 0.1s */
        nanosleep(&t, 0);
#else
        sleep(1);
#endif
        throw SQLiteBusy(format("%1%: %2%") % f.str() % sqlite3_errmsg(db));
    }
    else
        throw SQLiteError(format("%1%: %2%") % f.str() % sqlite3_errmsg(db));
}

SQLite::SQLite(const Path & path)
{
    if (sqlite3_open_v2(path.c_str(), &db,
            SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0) != SQLITE_OK)
        throw Error(format("cannot open SQLite database ‘%s’") % path);
}

SQLite::~SQLite()
{
    try {
        if (db && sqlite3_close(db) != SQLITE_OK)
            throwSQLiteError(db, "closing database");
    } catch (...) {
        ignoreException();
    }
}

void SQLite::exec(const std::string & stmt)
{
    if (sqlite3_exec(db, stmt.c_str(), 0, 0, 0) != SQLITE_OK)
        throwSQLiteError(db, format("executing SQLite statement ‘%s’") % stmt);
}

void SQLiteStmt::create(sqlite3 * db, const string & s)
{
    checkInterrupt();
    assert(!stmt);
    if (sqlite3_prepare_v2(db, s.c_str(), -1, &stmt, 0) != SQLITE_OK)
        throwSQLiteError(db, "creating statement");
    this->db = db;
}

SQLiteStmt::~SQLiteStmt()
{
    try {
        if (stmt && sqlite3_finalize(stmt) != SQLITE_OK)
            throwSQLiteError(db, "finalizing statement");
    } catch (...) {
        ignoreException();
    }
}

SQLiteStmt::Use::Use(SQLiteStmt & stmt)
    : stmt(stmt)
{
    assert(stmt.stmt);
    /* Note: sqlite3_reset() returns the error code for the most
       recent call to sqlite3_step().  So ignore it. */
    sqlite3_reset(stmt);
}

SQLiteStmt::Use::~Use()
{
    sqlite3_reset(stmt);
}

SQLiteStmt::Use & SQLiteStmt::Use::operator () (const std::string & value, bool notNull)
{
    if (notNull) {
        if (sqlite3_bind_text(stmt, curArg++, value.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
            throwSQLiteError(stmt.db, "binding argument");
    } else
        bind();
    return *this;
}

SQLiteStmt::Use & SQLiteStmt::Use::operator () (int64_t value, bool notNull)
{
    if (notNull) {
        if (sqlite3_bind_int64(stmt, curArg++, value) != SQLITE_OK)
            throwSQLiteError(stmt.db, "binding argument");
    } else
        bind();
    return *this;
}

SQLiteStmt::Use & SQLiteStmt::Use::bind()
{
    if (sqlite3_bind_null(stmt, curArg++) != SQLITE_OK)
        throwSQLiteError(stmt.db, "binding argument");
    return *this;
}

int SQLiteStmt::Use::step()
{
    return sqlite3_step(stmt);
}

void SQLiteStmt::Use::exec()
{
    int r = step();
    assert(r != SQLITE_ROW);
    if (r != SQLITE_DONE)
        throwSQLiteError(stmt.db, "executing SQLite statement");
}

bool SQLiteStmt::Use::next()
{
    int r = step();
    if (r != SQLITE_DONE && r != SQLITE_ROW)
        throwSQLiteError(stmt.db, "executing SQLite query");
    return r == SQLITE_ROW;
}

std::string SQLiteStmt::Use::getStr(int col)
{
    auto s = (const char *) sqlite3_column_text(stmt, col);
    assert(s);
    return s;
}

int64_t SQLiteStmt::Use::getInt(int col)
{
    // FIXME: detect nulls?
    return sqlite3_column_int64(stmt, col);
}

bool SQLiteStmt::Use::isNull(int col)
{
    return sqlite3_column_type(stmt, col) == SQLITE_NULL;
}

SQLiteTxn::SQLiteTxn(sqlite3 * db)
{
    this->db = db;
    if (sqlite3_exec(db, "begin;", 0, 0, 0) != SQLITE_OK)
        throwSQLiteError(db, "starting transaction");
    active = true;
}

void SQLiteTxn::commit()
{
    if (sqlite3_exec(db, "commit;", 0, 0, 0) != SQLITE_OK)
        throwSQLiteError(db, "committing transaction");
    active = false;
}

SQLiteTxn::~SQLiteTxn()
{
    try {
        if (active && sqlite3_exec(db, "rollback;", 0, 0, 0) != SQLITE_OK)
            throwSQLiteError(db, "aborting transaction");
    } catch (...) {
        ignoreException();
    }
}

}
<commit_msg>In SQLite errors, include the database path<commit_after>#include "sqlite.hh"
#include "util.hh"

#include <sqlite3.h>

namespace nix {

[[noreturn]] void throwSQLiteError(sqlite3 * db, const format & f)
{
    int err = sqlite3_errcode(db);

    auto path = sqlite3_db_filename(db, nullptr);
    if (!path) path = "(in-memory)";

    if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) {
        if (err == SQLITE_PROTOCOL)
            printError("warning: SQLite database ‘%s’ is busy (SQLITE_PROTOCOL)", path);
        else {
            static bool warned = false;
            if (!warned) {
                printError("warning: SQLite database ‘%s’ is busy", path);
                warned = true;
            }
        }
        /* Sleep for a while since retrying the transaction right away
           is likely to fail again. */
        checkInterrupt();
#if HAVE_NANOSLEEP
        struct timespec t;
        t.tv_sec = 0;
        t.tv_nsec = (random() % 100) * 1000 * 1000; /* <= 0.1s */
        nanosleep(&t, 0);
#else
        sleep(1);
#endif
        throw SQLiteBusy("%s: %s (in ‘%s’)", f.str(), sqlite3_errstr(err), path);
    }
    else
        throw SQLiteError("%s: %s (in ‘%s’)", f.str(), sqlite3_errstr(err), path);
}

SQLite::SQLite(const Path & path)
{
    if (sqlite3_open_v2(path.c_str(), &db,
            SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0) != SQLITE_OK)
        throw Error(format("cannot open SQLite database ‘%s’") % path);
}

SQLite::~SQLite()
{
    try {
        if (db && sqlite3_close(db) != SQLITE_OK)
            throwSQLiteError(db, "closing database");
    } catch (...) {
        ignoreException();
    }
}

void SQLite::exec(const std::string & stmt)
{
    if (sqlite3_exec(db, stmt.c_str(), 0, 0, 0) != SQLITE_OK)
        throwSQLiteError(db, format("executing SQLite statement ‘%s’") % stmt);
}

void SQLiteStmt::create(sqlite3 * db, const string & s)
{
    checkInterrupt();
    assert(!stmt);
    if (sqlite3_prepare_v2(db, s.c_str(), -1, &stmt, 0) != SQLITE_OK)
        throwSQLiteError(db, "creating statement");
    this->db = db;
}

SQLiteStmt::~SQLiteStmt()
{
    try {
        if (stmt && sqlite3_finalize(stmt) != SQLITE_OK)
            throwSQLiteError(db, "finalizing statement");
    } catch (...) {
        ignoreException();
    }
}

SQLiteStmt::Use::Use(SQLiteStmt & stmt)
    : stmt(stmt)
{
    assert(stmt.stmt);
    /* Note: sqlite3_reset() returns the error code for the most
       recent call to sqlite3_step().  So ignore it. */
    sqlite3_reset(stmt);
}

SQLiteStmt::Use::~Use()
{
    sqlite3_reset(stmt);
}

SQLiteStmt::Use & SQLiteStmt::Use::operator () (const std::string & value, bool notNull)
{
    if (notNull) {
        if (sqlite3_bind_text(stmt, curArg++, value.c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
            throwSQLiteError(stmt.db, "binding argument");
    } else
        bind();
    return *this;
}

SQLiteStmt::Use & SQLiteStmt::Use::operator () (int64_t value, bool notNull)
{
    if (notNull) {
        if (sqlite3_bind_int64(stmt, curArg++, value) != SQLITE_OK)
            throwSQLiteError(stmt.db, "binding argument");
    } else
        bind();
    return *this;
}

SQLiteStmt::Use & SQLiteStmt::Use::bind()
{
    if (sqlite3_bind_null(stmt, curArg++) != SQLITE_OK)
        throwSQLiteError(stmt.db, "binding argument");
    return *this;
}

int SQLiteStmt::Use::step()
{
    return sqlite3_step(stmt);
}

void SQLiteStmt::Use::exec()
{
    int r = step();
    assert(r != SQLITE_ROW);
    if (r != SQLITE_DONE)
        throwSQLiteError(stmt.db, "executing SQLite statement");
}

bool SQLiteStmt::Use::next()
{
    int r = step();
    if (r != SQLITE_DONE && r != SQLITE_ROW)
        throwSQLiteError(stmt.db, "executing SQLite query");
    return r == SQLITE_ROW;
}

std::string SQLiteStmt::Use::getStr(int col)
{
    auto s = (const char *) sqlite3_column_text(stmt, col);
    assert(s);
    return s;
}

int64_t SQLiteStmt::Use::getInt(int col)
{
    // FIXME: detect nulls?
    return sqlite3_column_int64(stmt, col);
}

bool SQLiteStmt::Use::isNull(int col)
{
    return sqlite3_column_type(stmt, col) == SQLITE_NULL;
}

SQLiteTxn::SQLiteTxn(sqlite3 * db)
{
    this->db = db;
    if (sqlite3_exec(db, "begin;", 0, 0, 0) != SQLITE_OK)
        throwSQLiteError(db, "starting transaction");
    active = true;
}

void SQLiteTxn::commit()
{
    if (sqlite3_exec(db, "commit;", 0, 0, 0) != SQLITE_OK)
        throwSQLiteError(db, "committing transaction");
    active = false;
}

SQLiteTxn::~SQLiteTxn()
{
    try {
        if (active && sqlite3_exec(db, "rollback;", 0, 0, 0) != SQLITE_OK)
            throwSQLiteError(db, "aborting transaction");
    } catch (...) {
        ignoreException();
    }
}

}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: composerdialogs.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: obo $ $Date: 2005-01-05 12:38:05 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef DBACCESS_SOURCE_UI_UNO_COMPOSERDIALOGS_HXX
#define DBACCESS_SOURCE_UI_UNO_COMPOSERDIALOGS_HXX

/** === begin UNO includes === **/
#ifndef _COM_SUN_STAR_CONTAINER_XNAMEACCESS_HPP_
#include <com/sun/star/container/XNameAccess.hpp>
#endif
#ifndef _COM_SUN_STAR_SDBC_XCONNECTION_HPP_
#include <com/sun/star/sdbc/XConnection.hpp>
#endif
#ifndef _COM_SUN_STAR_SDB_XSINGLESELECTQUERYCOMPOSER_HPP_
#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp>
#endif
#ifndef _COM_SUN_STAR_BEANS_XPROPERTYSET_HPP_
#include <com/sun/star/beans/XPropertySet.hpp>
#endif
#ifndef _COM_SUN_STAR_SDBC_XROWSET_HPP_
#include <com/sun/star/sdbc/XRowSet.hpp>
#endif
/** === end UNO includes === **/

#ifndef _SVT_GENERICUNODIALOG_HXX_
#include <svtools/genericunodialog.hxx>
#endif
#ifndef _DBAUI_MODULE_DBU_HXX_
#include "moduledbu.hxx"
#endif
#ifndef _DBASHARED_APITOOLS_HXX_
#include "apitools.hxx"
#endif

//.........................................................................
namespace dbaui
{
//.........................................................................

    //=====================================================================
    //= ComposerDialog
    //=====================================================================
    class ComposerDialog;
    typedef ::svt::OGenericUnoDialog                                    ComposerDialog_BASE;
    typedef ::comphelper::OPropertyArrayUsageHelper< ComposerDialog >  ComposerDialog_PBASE;

    class ComposerDialog
            :public ComposerDialog_BASE
            ,public ComposerDialog_PBASE
            ,public OModuleClient
    {
    protected:
        // <properties>
        ::com::sun::star::uno::Reference< ::com::sun::star::sdb::XSingleSelectQueryComposer >
                        m_xComposer;
        ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XRowSet >
                        m_xRowSet;
        // </properties>

    protected:
        ComposerDialog(const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxORB);
        ~ComposerDialog();

    public:
        DECLARE_IMPLEMENTATION_ID( );

        DECLARE_PROPERTYCONTAINER_DEFAULTS( );

    protected:
        // own overridables
        virtual Dialog* createComposerDialog(
            Window* _pParent,
            const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection,
            const ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameAccess >& _rxColumns
        ) = 0;

    private:
        // OGenericUnoDialog overridables
        virtual Dialog* createDialog(Window* _pParent);
    };

    //=====================================================================
    //= RowsetFilterDialog
    //=====================================================================
    class RowsetFilterDialog : public ComposerDialog
    {
    public:
        RowsetFilterDialog(
            const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxORB
        );

        DECLARE_SERVICE_INFO_STATIC( );

    protected:
        // own overridables
        virtual Dialog* createComposerDialog(
            Window* _pParent,
            const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection,
            const ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameAccess >& _rxColumns
        );

        // OGenericUnoDialog overridables
        virtual void executedDialog( sal_Int16 _nExecutionResult );
    };

    //=====================================================================
    //= RowsetOrderDialog
    //=====================================================================
    class RowsetOrderDialog : public ComposerDialog
    {
    public:
        RowsetOrderDialog(
            const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxORB
        );

        DECLARE_SERVICE_INFO_STATIC( );

    protected:
        // own overridables
        virtual Dialog* createComposerDialog(
            Window* _pParent,
            const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection,
            const ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameAccess >& _rxColumns
        );

        // OGenericUnoDialog overridables
        virtual void executedDialog( sal_Int16 _nExecutionResult );
    };

//.........................................................................
}   // namespace dbaui
//.........................................................................

#endif // DBACCESS_SOURCE_UI_UNO_COMPOSERDIALOGS_HXX

<commit_msg>INTEGRATION: CWS ooo19126 (1.2.146); FILE MERGED 2005/09/05 17:35:58 rt 1.2.146.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: composerdialogs.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 16:50:51 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef DBACCESS_SOURCE_UI_UNO_COMPOSERDIALOGS_HXX
#define DBACCESS_SOURCE_UI_UNO_COMPOSERDIALOGS_HXX

/** === begin UNO includes === **/
#ifndef _COM_SUN_STAR_CONTAINER_XNAMEACCESS_HPP_
#include <com/sun/star/container/XNameAccess.hpp>
#endif
#ifndef _COM_SUN_STAR_SDBC_XCONNECTION_HPP_
#include <com/sun/star/sdbc/XConnection.hpp>
#endif
#ifndef _COM_SUN_STAR_SDB_XSINGLESELECTQUERYCOMPOSER_HPP_
#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp>
#endif
#ifndef _COM_SUN_STAR_BEANS_XPROPERTYSET_HPP_
#include <com/sun/star/beans/XPropertySet.hpp>
#endif
#ifndef _COM_SUN_STAR_SDBC_XROWSET_HPP_
#include <com/sun/star/sdbc/XRowSet.hpp>
#endif
/** === end UNO includes === **/

#ifndef _SVT_GENERICUNODIALOG_HXX_
#include <svtools/genericunodialog.hxx>
#endif
#ifndef _DBAUI_MODULE_DBU_HXX_
#include "moduledbu.hxx"
#endif
#ifndef _DBASHARED_APITOOLS_HXX_
#include "apitools.hxx"
#endif

//.........................................................................
namespace dbaui
{
//.........................................................................

    //=====================================================================
    //= ComposerDialog
    //=====================================================================
    class ComposerDialog;
    typedef ::svt::OGenericUnoDialog                                    ComposerDialog_BASE;
    typedef ::comphelper::OPropertyArrayUsageHelper< ComposerDialog >  ComposerDialog_PBASE;

    class ComposerDialog
            :public ComposerDialog_BASE
            ,public ComposerDialog_PBASE
            ,public OModuleClient
    {
    protected:
        // <properties>
        ::com::sun::star::uno::Reference< ::com::sun::star::sdb::XSingleSelectQueryComposer >
                        m_xComposer;
        ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XRowSet >
                        m_xRowSet;
        // </properties>

    protected:
        ComposerDialog(const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxORB);
        ~ComposerDialog();

    public:
        DECLARE_IMPLEMENTATION_ID( );

        DECLARE_PROPERTYCONTAINER_DEFAULTS( );

    protected:
        // own overridables
        virtual Dialog* createComposerDialog(
            Window* _pParent,
            const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection,
            const ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameAccess >& _rxColumns
        ) = 0;

    private:
        // OGenericUnoDialog overridables
        virtual Dialog* createDialog(Window* _pParent);
    };

    //=====================================================================
    //= RowsetFilterDialog
    //=====================================================================
    class RowsetFilterDialog : public ComposerDialog
    {
    public:
        RowsetFilterDialog(
            const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxORB
        );

        DECLARE_SERVICE_INFO_STATIC( );

    protected:
        // own overridables
        virtual Dialog* createComposerDialog(
            Window* _pParent,
            const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection,
            const ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameAccess >& _rxColumns
        );

        // OGenericUnoDialog overridables
        virtual void executedDialog( sal_Int16 _nExecutionResult );
    };

    //=====================================================================
    //= RowsetOrderDialog
    //=====================================================================
    class RowsetOrderDialog : public ComposerDialog
    {
    public:
        RowsetOrderDialog(
            const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxORB
        );

        DECLARE_SERVICE_INFO_STATIC( );

    protected:
        // own overridables
        virtual Dialog* createComposerDialog(
            Window* _pParent,
            const ::com::sun::star::uno::Reference< ::com::sun::star::sdbc::XConnection >& _rxConnection,
            const ::com::sun::star::uno::Reference< ::com::sun::star::container::XNameAccess >& _rxColumns
        );

        // OGenericUnoDialog overridables
        virtual void executedDialog( sal_Int16 _nExecutionResult );
    };

//.........................................................................
}   // namespace dbaui
//.........................................................................

#endif // DBACCESS_SOURCE_UI_UNO_COMPOSERDIALOGS_HXX

<|endoftext|>


<commit_before>/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include <sstream>
#include "util/test.h"
#include "util/exception.h"
#include "util/escaped.h"
#include "frontends/lean/scanner.h"
using namespace lean;

#define st scanner::token

static void scan(char const * str, list<name> const & cmds = list<name>()) {
    std::istringstream in(str);
    scanner s(in);
    for (name const & n : cmds) s.add_command_keyword(n);
    while (true) {
        st t = s.scan();
        if (t == st::Eof)
            break;
        std::cout << t;
        if (t == st::Id || t == st::CommandId)
            std::cout << "[" << s.get_name_val() << "]";
        else if (t == st::IntVal || t == st::DecimalVal)
            std::cout << "[" << s.get_num_val() << "]";
        else if (t == st::StringVal)
            std::cout << "[\"" << escaped(s.get_str_val().c_str()) << "\"]";
        std::cout << " ";
    }
    std::cout << "\n";
}

static void scan_error(char const * str) {
    try {
        scan(str);
        lean_unreachable();
    } catch (exception & ex) {
        std::cout << "expected error: " << ex.what() << "\n";
    }
}

static void check(char const * str, std::initializer_list<scanner::token> const & l, list<name> const & cmds = list<name>()) {
    auto it = l.begin();
    std::istringstream in(str);
    scanner s(in);
    for (name const & n : cmds) s.add_command_keyword(n);
    while (true) {
        st t = s.scan();
        if (t == st::Eof) {
            lean_assert(it == l.end());
            return;
        }
        lean_assert(it != l.end());
        lean_assert(t == *it);
        ++it;
    }
}

static void check_name(char const * str, name const & expected) {
    std::istringstream in(str);
    scanner s(in);
    st t = s.scan();
    lean_assert(t == st::Id);
    lean_assert(s.get_name_val() == expected);
    lean_assert(s.scan() == st::Eof);
}

static void tst1() {
    scan("fun(x: Pi A : Type, A -> A), x+1 = 2.0 λ");
}

static void tst2() {
    scan("x::name");
    scan("x::10::foo");
    check("x::name", {st::Id});
    check("fun (x : Bool), x", {st::Lambda, st::LeftParen, st::Id, st::Colon, st::Id, st::RightParen, st::Comma, st::Id});
    check("+++", {st::Id});
    check("x+y", {st::Id, st::Id, st::Id});
    check("-- testing", {});
    check(" 2.31  ", {st::DecimalVal});
    check(" 333 22", {st::IntVal, st::IntVal});
    check("Int -> Int", {st::Id, st::Arrow, st::Id});
    check("Int -+-> Int", {st::Id, st::Id, st::Id});
    check("x := 10", {st::Id, st::Assign, st::IntVal});
    check("(x+1):Int", {st::LeftParen, st::Id, st::Id, st::IntVal, st::RightParen, st::Colon, st::Id});
    check("{x}", {st::LeftCurlyBracket, st::Id, st::RightCurlyBracket});
    check("\u03BB \u03A0 \u2192", {st::Lambda, st::Pi, st::Arrow});
    scan("++\u2295++x\u2296\u2296");
    check("++\u2295++x\u2296\u2296", {st::Id, st::Id, st::Id, st::Id, st::Id});
    scan("x10");
    check_name("x10", name("x10"));
    check_name("x::10", name(name("x"), 10));
    check_name("x::10::bla::0", name(name(name(name("x"), 10), "bla"), 0u));
    check("0::1", {st::IntVal, st::Colon, st::Colon, st::IntVal});
    check_name("\u2296\u2296", name("\u2296\u2296"));
    scan_error("x::1000000000000000000");
    scan("Theorem a : Bool Axiom b : Int", list<name>({"Theorem", "Axiom"}));
    check("Theorem a : Bool Axiom b : Int", {st::CommandId, st::Id, st::Colon, st::Id, st::CommandId, st::Id, st::Colon, st::Id}, list<name>({"Theorem", "Axiom"}));
    scan("foo \"tst\\\"\" : Int");
    check("foo \"tst\\\"\" : Int", {st::Id, st::StringVal, st::Colon, st::Id});
    scan_error("\"foo");
    scan("2.13 1.2 0.5");
}

static void tst3() {
    scan_error("\"\\");
    scan_error("\"\\a");
    scan("\"\naaa\"");
    scan_error("foo::0a::1");
    scan("10.0.");
    scan("{ } . forall exists let in \u2200 \u2203 := _");
    std::ostringstream out;
    out << scanner::token::Eof;
    lean_assert_eq(out.str(), "EOF");
}

int main() {
    save_stack_info();
    tst1();
    tst2();
    tst3();
    return has_violations() ? 1 : 0;
}
<commit_msg>test(frontends/lean/scanner): missing tests<commit_after>/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include <sstream>
#include "util/test.h"
#include "util/exception.h"
#include "util/escaped.h"
#include "frontends/lean/scanner.h"
using namespace lean;

#define st scanner::token

static void scan(char const * str, list<name> const & cmds = list<name>()) {
    std::istringstream in(str);
    scanner s(in);
    for (name const & n : cmds) s.add_command_keyword(n);
    while (true) {
        st t = s.scan();
        if (t == st::Eof)
            break;
        std::cout << t;
        if (t == st::Id || t == st::CommandId)
            std::cout << "[" << s.get_name_val() << "]";
        else if (t == st::IntVal || t == st::DecimalVal)
            std::cout << "[" << s.get_num_val() << "]";
        else if (t == st::StringVal)
            std::cout << "[\"" << escaped(s.get_str_val().c_str()) << "\"]";
        std::cout << " ";
    }
    std::cout << "\n";
}

static void scan_error(char const * str) {
    try {
        scan(str);
        lean_unreachable();
    } catch (exception & ex) {
        std::cout << "expected error: " << ex.what() << "\n";
    }
}

static void check(char const * str, std::initializer_list<scanner::token> const & l, list<name> const & cmds = list<name>()) {
    auto it = l.begin();
    std::istringstream in(str);
    scanner s(in);
    for (name const & n : cmds) s.add_command_keyword(n);
    while (true) {
        st t = s.scan();
        if (t == st::Eof) {
            lean_assert(it == l.end());
            return;
        }
        lean_assert(it != l.end());
        lean_assert(t == *it);
        ++it;
    }
}

static void check_name(char const * str, name const & expected) {
    std::istringstream in(str);
    scanner s(in);
    st t = s.scan();
    lean_assert(t == st::Id);
    lean_assert(s.get_name_val() == expected);
    lean_assert(s.scan() == st::Eof);
}

static void tst1() {
    scan("fun(x: Pi A : Type, A -> A), x+1 = 2.0 λ");
}

static void tst2() {
    scan("x::name");
    scan("x::10::foo");
    check("x::name", {st::Id});
    check("fun (x : Bool), x", {st::Lambda, st::LeftParen, st::Id, st::Colon, st::Id, st::RightParen, st::Comma, st::Id});
    check("+++", {st::Id});
    check("x+y", {st::Id, st::Id, st::Id});
    check("-- testing", {});
    check(" 2.31  ", {st::DecimalVal});
    check(" 333 22", {st::IntVal, st::IntVal});
    check("Int -> Int", {st::Id, st::Arrow, st::Id});
    check("Int -+-> Int", {st::Id, st::Id, st::Id});
    check("x := 10", {st::Id, st::Assign, st::IntVal});
    check("(x+1):Int", {st::LeftParen, st::Id, st::Id, st::IntVal, st::RightParen, st::Colon, st::Id});
    check("{x}", {st::LeftCurlyBracket, st::Id, st::RightCurlyBracket});
    check("\u03BB \u03A0 \u2192", {st::Lambda, st::Pi, st::Arrow});
    scan("++\u2295++x\u2296\u2296");
    check("++\u2295++x\u2296\u2296", {st::Id, st::Id, st::Id, st::Id, st::Id});
    scan("x10 ... == (* print('hello') *) have by");
    check_name("x10", name("x10"));
    check_name("x::10", name(name("x"), 10));
    check_name("x::10::bla::0", name(name(name(name("x"), 10), "bla"), 0u));
    check("0::1", {st::IntVal, st::Colon, st::Colon, st::IntVal});
    check_name("\u2296\u2296", name("\u2296\u2296"));
    scan_error("x::1000000000000000000");
    scan("Theorem a : Bool Axiom b : Int", list<name>({"Theorem", "Axiom"}));
    check("Theorem a : Bool Axiom b : Int", {st::CommandId, st::Id, st::Colon, st::Id, st::CommandId, st::Id, st::Colon, st::Id}, list<name>({"Theorem", "Axiom"}));
    scan("foo \"tst\\\"\" : Int");
    check("foo \"tst\\\"\" : Int", {st::Id, st::StringVal, st::Colon, st::Id});
    scan_error("\"foo");
    scan("2.13 1.2 0.5");
}

static void tst3() {
    scan_error("\"\\");
    scan_error("\"\\a");
    scan("\"\naaa\"");
    scan_error("foo::0a::1");
    scan("10.0.");
    scan("{ } . forall exists let in \u2200 \u2203 := _");
    std::ostringstream out;
    out << scanner::token::Eof;
    lean_assert_eq(out.str(), "EOF");
}

int main() {
    save_stack_info();
    tst1();
    tst2();
    tst3();
    return has_violations() ? 1 : 0;
}
<|endoftext|>


<commit_before>/*
 *  Copyright 2008-2013 NVIDIA Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#pragma once

#include <thrust/system/cuda/detail/bulk/detail/config.hpp>
#include <thrust/system/cuda/detail/bulk/detail/pointer_traits.hpp>
#include <thrust/system/cuda/detail/bulk/detail/alignment.hpp>
#include <thrust/system/cuda/detail/bulk/uninitialized.hpp>
#include <thrust/detail/config.h>
#include <cstdlib>

BULK_NAMESPACE_PREFIX
namespace bulk
{

inline __device__ bool is_on_chip(void *ptr)
{
  return bulk::detail::is_shared(ptr);
} // end is_on_chip()

template<typename T>
inline __device__ T *on_chip_cast(T *ptr)
{
  extern __shared__ char s_begin[];
  void *result = (reinterpret_cast<char*>(ptr) - s_begin) + s_begin;
  return reinterpret_cast<T*>(result);
} // end on_chip_cast()

namespace detail
{

extern __shared__ int s_data_segment_begin[];

class os
{
  public:
    __device__ inline os(size_t max_data_segment_size)
      : m_program_break(s_data_segment_begin),
        m_max_data_segment_size(max_data_segment_size)
    {
    }

    __device__ inline int brk(void *end_data_segment)
    {
      if(end_data_segment <= m_program_break)
      {
        m_program_break = end_data_segment;
        return 0;
      }

      return -1;
    }

    __device__ inline void *sbrk(size_t increment)
    {
      if(data_segment_size() + increment <= m_max_data_segment_size)
      {
        m_program_break = reinterpret_cast<char*>(m_program_break) + increment;
      } // end if
      else
      {
        return reinterpret_cast<void*>(-1);
      } // end else

      return m_program_break;
    }

    __device__ inline void *program_break() const
    {
      return m_program_break;
    }

    __device__ inline void *data_segment_begin() const
    {
      return s_data_segment_begin;
    }

  private:
    __device__ inline size_t data_segment_size()
    {
      return reinterpret_cast<char*>(m_program_break) - reinterpret_cast<char*>(s_data_segment_begin);
    } // end data_segment_size()

    void *m_program_break;

    // XXX this can safely be uint32
    size_t m_max_data_segment_size;
};

// only one instance of this class can logically exist per CTA, and its use is thread-unsafe
class singleton_unsafe_on_chip_allocator
{
  public:
    __device__ inline singleton_unsafe_on_chip_allocator(size_t max_data_segment_size)
      : m_os(max_data_segment_size)
    {}

    __device__ inline void *allocate(size_t size)
    {
      size_t aligned_size = align8(size);

      block *prev = find_first_free_insertion_point(heap_begin(), heap_end(), aligned_size);

      block *b;

      if(prev != heap_end() && (b = prev->next()) != heap_end())
      {
        // can we split?
        if((b->size() - aligned_size) >= sizeof(block))
        {
          split_block(b, aligned_size);
        } // end if

        b->set_is_free(false);
      } // end if
      else
      {
        // nothing fits, extend the heap
        b = extend_heap(prev, aligned_size);
        if(b == heap_end())
        {
          return 0;
        } // end if
      } // end else

      return b->data();
    } // end allocate()

    __device__ inline void deallocate(void *ptr)
    {
      if(ptr != 0)
      {
        block *b = get_block(ptr);

        // free the block
        b->set_is_free(true);

        // try to fuse the freed block the previous block
        if(b->prev() && b->prev()->is_free())
        {
          b = b->prev();
          fuse_block(b);
        } // end if

        // now try to fuse with the next block
        if(b->next() != heap_end())
        {
          fuse_block(b);
        } // end if
        else
        {
          // the the OS know where the new break is
          m_os.brk(b);
        } // end else
      } // end if
    } // end deallocate()

  private:
    // align to two words
    class block : public bulk::detail::aligned_type<sizeof(size_t) + sizeof(block*)>::type
    {
      public:
        __device__ inline size_t size() const
        {
          return m_size;
        } // end size()

        __device__ void set_size(size_t sz)
        {
          m_size = sz;
        } // end set_size()

        __device__ inline block *prev() const
        {
          return m_prev;
        } // end prev()

        __device__ void set_prev(block *p)
        {
          m_prev = p;
        } // end set_prev()

        // returns a pointer to the indexth byte within this block's data
        __device__ inline void *byte_at(size_t index) const
        {
          return reinterpret_cast<char*>(data()) + index;
        } // end byte_at()

        __device__ inline block *next() const
        {
          return reinterpret_cast<block*>(byte_at(size()));
        } // end next()

        __device__ inline bool is_free() const
        {
          return m_is_free;
        } // end is_free()

        __device__ inline void set_is_free(bool f)
        {
          m_is_free = f;
        } // end set_is_free()

        __device__ inline void *data() const
        {
          return reinterpret_cast<char*>(const_cast<block*>(this)) + sizeof(block);
        } // end data()

      private:
        // this packing ensures that sizeof(block) is compatible with 64b alignment, because:
        // on a 32b platform, sizeof(block) == 64b
        // on a 64b platform, sizeof(block) == 128b
        bool   m_is_free : 1;
        size_t m_size    : 8 * sizeof(size_t) - 1;
        block *m_prev;
    };

    os     m_os;

    __device__ inline block *heap_begin() const
    {
      return reinterpret_cast<block*>(m_os.data_segment_begin());
    } // end heap_begin()

    __device__ inline block *heap_end() const
    {
      return reinterpret_cast<block*>(m_os.program_break());
    } // end heap_end();

    __device__ inline void split_block(block *b, size_t size)
    {
      block *new_block;

      // emplace a new block within the old one's data segment
      new_block = reinterpret_cast<block*>(b->byte_at(size));

      // the new block's size is the old block's size less the size of the split less the size of a block
      new_block->set_size(b->size() - size - sizeof(block));

      new_block->set_prev(b);
      new_block->set_is_free(true);

      // the old block's size is the size of the split
      b->set_size(size);

      // link the old block to the new one
      if(new_block->next() != heap_end())
      {
        new_block->next()->set_prev(new_block);
      } // end if
    } // end split_block()

    __device__ inline bool fuse_block(block *b)
    {
      if(b->next() != heap_end() && b->next()->is_free())
      {
        // increment b's size by sizeof(block) plus the next's block's data size
        b->set_size(sizeof(block) + b->next()->size() + b->size());

        if(b->next() != heap_end())
        {
          b->next()->set_prev(b);
        }

        return true;
      }

      return false;
    } // end fuse_block()

    __device__ inline static block *get_block(void *data)
    {
      // the block metadata lives sizeof(block) bytes to the left of data
      void *ptr = reinterpret_cast<char*>(data) - sizeof(block);
      return reinterpret_cast<block *>(ptr);
    } // end get_block()

    __device__ inline static block *find_first_free_insertion_point(block *first, block *last, size_t size)
    {
      block *prev = last;

      while(first != last && !(first->is_free() && first->size() >= size))
      {
        prev = first;
        first = first->next();
      }

      return prev;
    } // end find_first_free_insertion_point()

    __device__ inline block *extend_heap(block *prev, size_t size)
    {
      // the new block goes at the current end of the heap
      block *new_block = heap_end();

      // move the break to the right to accomodate both a block and the requested allocation
      if(m_os.sbrk(sizeof(block) + size) == reinterpret_cast<void*>(-1))
      {
        // allocation failed
        return new_block;
      }

      on_chip_cast(new_block)->set_size(size);
      on_chip_cast(new_block)->set_prev(prev);
      on_chip_cast(new_block)->set_is_free(false);

      return new_block;
    } // end extend_heap()

    __device__ inline static size_t align8(size_t size)
    {
      return ((((size - 1) >> 3) << 3) + 8);
    } // end align4()
}; // end singleton_unsafe_on_chip_allocator

class singleton_on_chip_allocator
{
  public:
    inline __device__
    singleton_on_chip_allocator(size_t max_data_segment_size)
      : m_mutex(),
        m_alloc(max_data_segment_size)
    {}

    inline __device__
    void *unsafe_allocate(size_t size)
    {
      return m_alloc.allocate(size);
    }

    inline __device__
    void *allocate(size_t size)
    {
      void *result;

      m_mutex.lock();
      {
        result = unsafe_allocate(size);
      } // end critical section
      m_mutex.unlock();

      return result;
    } // end allocate()

    inline __device__
    void unsafe_deallocate(void *ptr)
    {
      m_alloc.deallocate(ptr);
    } // end unsafe_deallocate()

    inline __device__
    void deallocate(void *ptr)
    {
      m_mutex.lock();
      {
        unsafe_deallocate(ptr);
      } // end critical section
      m_mutex.unlock();
    } // end deallocate()

  private:
    class mutex
    {
      public:
        inline __device__
        mutex()
          : m_in_use(0)
        {}

        inline __device__
        bool try_lock()
        {
#if __CUDA_ARCH__ >= 110
          return atomicCAS(&m_in_use, 0, 1) != 0;
#else
          return false;
#endif
        } // end try_lock()

        inline __device__
        void lock()
        {
          // spin while waiting
          while(try_lock())
          {
            ;
          }
        } // end lock()

        inline __device__
        void unlock()
        {
          m_in_use = 0;
        } // end unlock()

      private:
        unsigned int m_in_use;
    }; // end mutex

    mutex m_mutex;
    singleton_unsafe_on_chip_allocator m_alloc;
}; // end singleton_on_chip_allocator

// put the object in an anonymous namespace so that non-CUDA compilers don't complain about multiple definitions
namespace
{

__shared__  uninitialized<singleton_on_chip_allocator> s_on_chip_allocator;

} // end anon namespace

inline __device__ void init_on_chip_malloc(size_t max_data_segment_size)
{
  s_on_chip_allocator.construct(max_data_segment_size);
} // end init_on_chip_malloc()

inline __device__ void *on_chip_malloc(size_t size)
{
  void *result = s_on_chip_allocator.get().allocate(size);
  return on_chip_cast(result);
} // end on_chip_malloc()

inline __device__ void on_chip_free(void *ptr)
{
  s_on_chip_allocator.get().deallocate(ptr);
} // end on_chip_free()

inline __device__ void *unsafe_on_chip_malloc(size_t size)
{
  void *result = s_on_chip_allocator.get().unsafe_allocate(size);
  return on_chip_cast(result);
} // end unsafe_on_chip_malloc()

inline __device__ void unsafe_on_chip_free(void *ptr)
{
  s_on_chip_allocator.get().unsafe_deallocate(ptr);
} // end unsafe_on_chip_free()

} // end detail

inline __device__ void *shmalloc(size_t num_bytes)
{
  // first try on_chip_malloc
  void *result = detail::on_chip_malloc(num_bytes);

#if __CUDA_ARCH__ >= 200
  if(!result)
  {
    result = std::malloc(num_bytes);
  } // end if
#endif // __CUDA_ARCH__

  return result;
} // end shmalloc()

inline __device__ void *unsafe_shmalloc(size_t num_bytes)
{
  // first try on_chip_malloc
  void *result = detail::unsafe_on_chip_malloc(num_bytes);

#if __CUDA_ARCH__ >= 200
  if(!result)
  {
    result = std::malloc(num_bytes);
  } // end if
#endif // __CUDA_ARCH__

  return result;
} // end unsafe_shmalloc()

inline __device__ void shfree(void *ptr)
{
#if __CUDA_ARCH__ >= 200
  if(bulk::is_on_chip(ptr))
  {
    bulk::detail::on_chip_free(bulk::on_chip_cast(ptr));
  } // end if
  else
  {
    std::free(ptr);
  } // end else
#else
  bulk::detail::on_chip_free(bulk::on_chip_cast(ptr));
#endif
} // end shfree()

inline __device__ void unsafe_shfree(void *ptr)
{
#if __CUDA_ARCH__ >= 200
  if(bulk::is_on_chip(ptr))
  {
    bulk::detail::unsafe_on_chip_free(bulk::on_chip_cast(ptr));
  } // end if
  else
  {
    std::free(ptr);
  } // end else
#else
  bulk::detail::unsafe_on_chip_free(bulk::on_chip_cast(ptr));
#endif
} // end unsafe_shfree()

template<typename ConcurrentGroup>
__device__
inline void *malloc(ConcurrentGroup &g, size_t num_bytes)
{
  __shared__ void *s_result;

  // we need to guard access to s_result from other
  // invocations of malloc, so we put a wait at the beginning
  g.wait();

  if(g.this_exec.index() == 0)
  {
    s_result = bulk::unsafe_shmalloc(num_bytes);
  } // end if

  g.wait();

  return s_result;
} // end malloc()

template<typename ConcurrentGroup>
__device__
inline void free(ConcurrentGroup &g, void *ptr)
{
  if(g.this_exec.index() == 0)
  {
    bulk::unsafe_shfree(ptr);
  } // end if

  g.wait();
} // end free()

} // end namespace bulk
BULK_NAMESPACE_SUFFIX

<commit_msg>Restore WAR to singleton_on_chip_allocator's constructor<commit_after>/*
 *  Copyright 2008-2013 NVIDIA Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#pragma once

#include <thrust/system/cuda/detail/bulk/detail/config.hpp>
#include <thrust/system/cuda/detail/bulk/detail/pointer_traits.hpp>
#include <thrust/system/cuda/detail/bulk/detail/alignment.hpp>
#include <thrust/system/cuda/detail/bulk/uninitialized.hpp>
#include <thrust/detail/config.h>
#include <cstdlib>

BULK_NAMESPACE_PREFIX
namespace bulk
{

inline __device__ bool is_on_chip(void *ptr)
{
  return bulk::detail::is_shared(ptr);
} // end is_on_chip()

template<typename T>
inline __device__ T *on_chip_cast(T *ptr)
{
  extern __shared__ char s_begin[];
  void *result = (reinterpret_cast<char*>(ptr) - s_begin) + s_begin;
  return reinterpret_cast<T*>(result);
} // end on_chip_cast()

namespace detail
{

extern __shared__ int s_data_segment_begin[];

class os
{
  public:
    __device__ inline os(size_t max_data_segment_size)
      : m_program_break(s_data_segment_begin),
        m_max_data_segment_size(max_data_segment_size)
    {
    }

    __device__ inline int brk(void *end_data_segment)
    {
      if(end_data_segment <= m_program_break)
      {
        m_program_break = end_data_segment;
        return 0;
      }

      return -1;
    }

    __device__ inline void *sbrk(size_t increment)
    {
      if(data_segment_size() + increment <= m_max_data_segment_size)
      {
        m_program_break = reinterpret_cast<char*>(m_program_break) + increment;
      } // end if
      else
      {
        return reinterpret_cast<void*>(-1);
      } // end else

      return m_program_break;
    }

    __device__ inline void *program_break() const
    {
      return m_program_break;
    }

    __device__ inline void *data_segment_begin() const
    {
      return s_data_segment_begin;
    }

  private:
    __device__ inline size_t data_segment_size()
    {
      return reinterpret_cast<char*>(m_program_break) - reinterpret_cast<char*>(s_data_segment_begin);
    } // end data_segment_size()

    void *m_program_break;

    // XXX this can safely be uint32
    size_t m_max_data_segment_size;
};

// only one instance of this class can logically exist per CTA, and its use is thread-unsafe
class singleton_unsafe_on_chip_allocator
{
  public:
    __device__ inline singleton_unsafe_on_chip_allocator(size_t max_data_segment_size)
      : m_os(max_data_segment_size)
    {}

    __device__ inline void *allocate(size_t size)
    {
      size_t aligned_size = align8(size);

      block *prev = find_first_free_insertion_point(heap_begin(), heap_end(), aligned_size);

      block *b;

      if(prev != heap_end() && (b = prev->next()) != heap_end())
      {
        // can we split?
        if((b->size() - aligned_size) >= sizeof(block))
        {
          split_block(b, aligned_size);
        } // end if

        b->set_is_free(false);
      } // end if
      else
      {
        // nothing fits, extend the heap
        b = extend_heap(prev, aligned_size);
        if(b == heap_end())
        {
          return 0;
        } // end if
      } // end else

      return b->data();
    } // end allocate()

    __device__ inline void deallocate(void *ptr)
    {
      if(ptr != 0)
      {
        block *b = get_block(ptr);

        // free the block
        b->set_is_free(true);

        // try to fuse the freed block the previous block
        if(b->prev() && b->prev()->is_free())
        {
          b = b->prev();
          fuse_block(b);
        } // end if

        // now try to fuse with the next block
        if(b->next() != heap_end())
        {
          fuse_block(b);
        } // end if
        else
        {
          // the the OS know where the new break is
          m_os.brk(b);
        } // end else
      } // end if
    } // end deallocate()

  private:
    // align to two words
    class block : public bulk::detail::aligned_type<sizeof(size_t) + sizeof(block*)>::type
    {
      public:
        __device__ inline size_t size() const
        {
          return m_size;
        } // end size()

        __device__ void set_size(size_t sz)
        {
          m_size = sz;
        } // end set_size()

        __device__ inline block *prev() const
        {
          return m_prev;
        } // end prev()

        __device__ void set_prev(block *p)
        {
          m_prev = p;
        } // end set_prev()

        // returns a pointer to the indexth byte within this block's data
        __device__ inline void *byte_at(size_t index) const
        {
          return reinterpret_cast<char*>(data()) + index;
        } // end byte_at()

        __device__ inline block *next() const
        {
          return reinterpret_cast<block*>(byte_at(size()));
        } // end next()

        __device__ inline bool is_free() const
        {
          return m_is_free;
        } // end is_free()

        __device__ inline void set_is_free(bool f)
        {
          m_is_free = f;
        } // end set_is_free()

        __device__ inline void *data() const
        {
          return reinterpret_cast<char*>(const_cast<block*>(this)) + sizeof(block);
        } // end data()

      private:
        // this packing ensures that sizeof(block) is compatible with 64b alignment, because:
        // on a 32b platform, sizeof(block) == 64b
        // on a 64b platform, sizeof(block) == 128b
        bool   m_is_free : 1;
        size_t m_size    : 8 * sizeof(size_t) - 1;
        block *m_prev;
    };

    os     m_os;

    __device__ inline block *heap_begin() const
    {
      return reinterpret_cast<block*>(m_os.data_segment_begin());
    } // end heap_begin()

    __device__ inline block *heap_end() const
    {
      return reinterpret_cast<block*>(m_os.program_break());
    } // end heap_end();

    __device__ inline void split_block(block *b, size_t size)
    {
      block *new_block;

      // emplace a new block within the old one's data segment
      new_block = reinterpret_cast<block*>(b->byte_at(size));

      // the new block's size is the old block's size less the size of the split less the size of a block
      new_block->set_size(b->size() - size - sizeof(block));

      new_block->set_prev(b);
      new_block->set_is_free(true);

      // the old block's size is the size of the split
      b->set_size(size);

      // link the old block to the new one
      if(new_block->next() != heap_end())
      {
        new_block->next()->set_prev(new_block);
      } // end if
    } // end split_block()

    __device__ inline bool fuse_block(block *b)
    {
      if(b->next() != heap_end() && b->next()->is_free())
      {
        // increment b's size by sizeof(block) plus the next's block's data size
        b->set_size(sizeof(block) + b->next()->size() + b->size());

        if(b->next() != heap_end())
        {
          b->next()->set_prev(b);
        }

        return true;
      }

      return false;
    } // end fuse_block()

    __device__ inline static block *get_block(void *data)
    {
      // the block metadata lives sizeof(block) bytes to the left of data
      void *ptr = reinterpret_cast<char*>(data) - sizeof(block);
      return reinterpret_cast<block *>(ptr);
    } // end get_block()

    __device__ inline static block *find_first_free_insertion_point(block *first, block *last, size_t size)
    {
      block *prev = last;

      while(first != last && !(first->is_free() && first->size() >= size))
      {
        prev = first;
        first = first->next();
      }

      return prev;
    } // end find_first_free_insertion_point()

    __device__ inline block *extend_heap(block *prev, size_t size)
    {
      // the new block goes at the current end of the heap
      block *new_block = heap_end();

      // move the break to the right to accomodate both a block and the requested allocation
      if(m_os.sbrk(sizeof(block) + size) == reinterpret_cast<void*>(-1))
      {
        // allocation failed
        return new_block;
      }

      on_chip_cast(new_block)->set_size(size);
      on_chip_cast(new_block)->set_prev(prev);
      on_chip_cast(new_block)->set_is_free(false);

      return new_block;
    } // end extend_heap()

    __device__ inline static size_t align8(size_t size)
    {
      return ((((size - 1) >> 3) << 3) + 8);
    } // end align4()
}; // end singleton_unsafe_on_chip_allocator

class singleton_on_chip_allocator
{
  public:
    // XXX mark as __host__ to WAR a warning from uninitialized.construct
    inline __device__ __host__
    singleton_on_chip_allocator(size_t max_data_segment_size)
      : m_mutex(),
        m_alloc(max_data_segment_size)
    {}

    inline __device__
    void *unsafe_allocate(size_t size)
    {
      return m_alloc.allocate(size);
    }

    inline __device__
    void *allocate(size_t size)
    {
      void *result;

      m_mutex.lock();
      {
        result = unsafe_allocate(size);
      } // end critical section
      m_mutex.unlock();

      return result;
    } // end allocate()

    inline __device__
    void unsafe_deallocate(void *ptr)
    {
      m_alloc.deallocate(ptr);
    } // end unsafe_deallocate()

    inline __device__
    void deallocate(void *ptr)
    {
      m_mutex.lock();
      {
        unsafe_deallocate(ptr);
      } // end critical section
      m_mutex.unlock();
    } // end deallocate()

  private:
    class mutex
    {
      public:
        inline __device__
        mutex()
          : m_in_use(0)
        {}

        inline __device__
        bool try_lock()
        {
#if __CUDA_ARCH__ >= 110
          return atomicCAS(&m_in_use, 0, 1) != 0;
#else
          return false;
#endif
        } // end try_lock()

        inline __device__
        void lock()
        {
          // spin while waiting
          while(try_lock())
          {
            ;
          }
        } // end lock()

        inline __device__
        void unlock()
        {
          m_in_use = 0;
        } // end unlock()

      private:
        unsigned int m_in_use;
    }; // end mutex

    mutex m_mutex;
    singleton_unsafe_on_chip_allocator m_alloc;
}; // end singleton_on_chip_allocator

// put the object in an anonymous namespace so that non-CUDA compilers don't complain about multiple definitions
namespace
{

__shared__  uninitialized<singleton_on_chip_allocator> s_on_chip_allocator;

} // end anon namespace

inline __device__ void init_on_chip_malloc(size_t max_data_segment_size)
{
  s_on_chip_allocator.construct(max_data_segment_size);
} // end init_on_chip_malloc()

inline __device__ void *on_chip_malloc(size_t size)
{
  void *result = s_on_chip_allocator.get().allocate(size);
  return on_chip_cast(result);
} // end on_chip_malloc()

inline __device__ void on_chip_free(void *ptr)
{
  s_on_chip_allocator.get().deallocate(ptr);
} // end on_chip_free()

inline __device__ void *unsafe_on_chip_malloc(size_t size)
{
  void *result = s_on_chip_allocator.get().unsafe_allocate(size);
  return on_chip_cast(result);
} // end unsafe_on_chip_malloc()

inline __device__ void unsafe_on_chip_free(void *ptr)
{
  s_on_chip_allocator.get().unsafe_deallocate(ptr);
} // end unsafe_on_chip_free()

} // end detail

inline __device__ void *shmalloc(size_t num_bytes)
{
  // first try on_chip_malloc
  void *result = detail::on_chip_malloc(num_bytes);

#if __CUDA_ARCH__ >= 200
  if(!result)
  {
    result = std::malloc(num_bytes);
  } // end if
#endif // __CUDA_ARCH__

  return result;
} // end shmalloc()

inline __device__ void *unsafe_shmalloc(size_t num_bytes)
{
  // first try on_chip_malloc
  void *result = detail::unsafe_on_chip_malloc(num_bytes);

#if __CUDA_ARCH__ >= 200
  if(!result)
  {
    result = std::malloc(num_bytes);
  } // end if
#endif // __CUDA_ARCH__

  return result;
} // end unsafe_shmalloc()

inline __device__ void shfree(void *ptr)
{
#if __CUDA_ARCH__ >= 200
  if(bulk::is_on_chip(ptr))
  {
    bulk::detail::on_chip_free(bulk::on_chip_cast(ptr));
  } // end if
  else
  {
    std::free(ptr);
  } // end else
#else
  bulk::detail::on_chip_free(bulk::on_chip_cast(ptr));
#endif
} // end shfree()

inline __device__ void unsafe_shfree(void *ptr)
{
#if __CUDA_ARCH__ >= 200
  if(bulk::is_on_chip(ptr))
  {
    bulk::detail::unsafe_on_chip_free(bulk::on_chip_cast(ptr));
  } // end if
  else
  {
    std::free(ptr);
  } // end else
#else
  bulk::detail::unsafe_on_chip_free(bulk::on_chip_cast(ptr));
#endif
} // end unsafe_shfree()

template<typename ConcurrentGroup>
__device__
inline void *malloc(ConcurrentGroup &g, size_t num_bytes)
{
  __shared__ void *s_result;

  // we need to guard access to s_result from other
  // invocations of malloc, so we put a wait at the beginning
  g.wait();

  if(g.this_exec.index() == 0)
  {
    s_result = bulk::unsafe_shmalloc(num_bytes);
  } // end if

  g.wait();

  return s_result;
} // end malloc()

template<typename ConcurrentGroup>
__device__
inline void free(ConcurrentGroup &g, void *ptr)
{
  if(g.this_exec.index() == 0)
  {
    bulk::unsafe_shfree(ptr);
  } // end if

  g.wait();
} // end free()

} // end namespace bulk
BULK_NAMESPACE_SUFFIX

<|endoftext|>


<commit_before>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "ThumbnailerWorker.h"

#include "Media.h"
#include "File.h"
#include "logging/Logger.h"
#include "MediaLibrary.h"
#include "medialibrary/filesystem/Errors.h"
#include "utils/Filename.h"
#include "utils/File.h"
#include "medialibrary/IThumbnailer.h"

#include <algorithm>

namespace medialibrary
{

ThumbnailerWorker::ThumbnailerWorker( MediaLibraryPtr ml,
                                      std::shared_ptr<IThumbnailer> thumbnailer )
    : m_ml( ml )
    , m_run( false )
    , m_generator( std::move( thumbnailer ) )
    , m_paused( false )
{
}

ThumbnailerWorker::~ThumbnailerWorker()
{
    stop();
}

void ThumbnailerWorker::requestThumbnailInternal( int64_t mediaId, MediaPtr media,
                                                  ThumbnailSizeType sizeType,
                                                  uint32_t desiredWidth,
                                                  uint32_t desiredHeight,
                                                  float position )
{
    std::unique_lock<compat::Mutex> lock( m_mutex );

    if ( m_queuedMedia.find( mediaId ) != cend( m_queuedMedia ) )
        return;

    Task t{
        mediaId,
        std::move( media ),
        sizeType,
        desiredWidth,
        desiredHeight,
        position
    };
    m_queuedMedia.insert( mediaId );
    m_tasks.push( std::move( t ) );
    assert( m_tasks.size() == m_queuedMedia.size() );
    if ( m_thread.get_id() == compat::Thread::id{} )
    {
        m_run = true;
        m_thread = compat::Thread( &ThumbnailerWorker::run, this );
    }
    else
        m_cond.notify_all();
}

void ThumbnailerWorker::runCleanupRequests()
{
    auto requests = Thumbnail::fetchCleanups( m_ml );
    for ( const auto& r : requests )
    {
        auto path = m_ml->thumbnailPath() + r.second;
        LOG_DEBUG( "Running cleanup request #", r.first, ": removing ", path );
        if ( !utils::fs::remove( path ) )
        {
            try
            {
                utils::fs::fileSize( path );
                /*
                 * If we can compute the file size, the file still exists so we
                 * should try to delete it again later
                 */
                continue;
            }
            catch ( const medialibrary::fs::errors::Exception& )
            {
                /*
                 * Let's assume the file doesn't exist anymore so we can still
                 * remove the cleanup request.
                 */
            }
        }
        if ( Thumbnail::removeCleanupRequest( m_ml, r.first ) == false )
            LOG_WARN( "Failed to remove thumbnail cleanup request #", r.first );
    }
}

void ThumbnailerWorker::requestThumbnail( int64_t mediaId, ThumbnailSizeType sizeType,
                                          uint32_t desiredWidth, uint32_t desiredHeight,
                                          float position )
{
    requestThumbnailInternal( mediaId, nullptr, sizeType, desiredWidth, desiredHeight, position );
}

void ThumbnailerWorker::requestCleanupRun()
{
    requestThumbnailInternal( 0, nullptr, ThumbnailSizeType::Thumbnail,
                              0, 0, 0.f );
}

void ThumbnailerWorker::requestThumbnail( MediaPtr media, ThumbnailSizeType sizeType,
                                          uint32_t desiredWidth, uint32_t desiredHeight,
                                          float position )
{
    /* Call media->id() before moving media */
    int64_t mediaId = media->id();
    requestThumbnailInternal( mediaId, std::move(media), sizeType, desiredWidth, desiredHeight, position );
}

void ThumbnailerWorker::pause()
{
    std::lock_guard<compat::Mutex> lock( m_mutex );
    m_paused = true;
}

void ThumbnailerWorker::resume()
{
    std::lock_guard<compat::Mutex> lock( m_mutex );
    if ( m_paused == false )
        return;
    m_paused = false;
    m_cond.notify_all();
}

void ThumbnailerWorker::run()
{
    LOG_INFO( "Starting thumbnailer thread" );
    while ( m_run == true )
    {
        ML_UNHANDLED_EXCEPTION_INIT
        {
            Task t;
            {
                std::unique_lock<compat::Mutex> lock( m_mutex );
                if ( m_tasks.size() == 0 || m_paused == true )
                {
                    m_cond.wait( lock, [this]() {
                        return ( m_tasks.size() > 0 && m_paused == false ) ||
                                m_run == false;
                    });
                    if ( m_run == false )
                        break;
                }
                t = std::move( m_tasks.front() );
                m_tasks.pop();
                m_queuedMedia.erase( t.mediaId );
                assert( m_tasks.size() == m_queuedMedia.size() );
            }
            if ( t.mediaId == 0 )
                runCleanupRequests();
            if ( t.media == nullptr )
            {
                t.media = m_ml->media( t.mediaId );
                if ( t.media == nullptr )
                    /* No media found with this id */
                    continue;
            }
            bool res = generateThumbnail( t );
            m_ml->getCb()->onMediaThumbnailReady( t.media, t.sizeType, res );
        }
        ML_UNHANDLED_EXCEPTION_BODY( "ThumbnailerWorker" )
    }
    LOG_INFO( "Exiting thumbnailer thread" );
}

void ThumbnailerWorker::stop()
{
    bool running = true;
    if ( m_run.compare_exchange_strong( running, false ) )
    {
        m_generator->stop();
        {
            std::unique_lock<compat::Mutex> lock( m_mutex );
            while ( m_tasks.empty() == false )
                m_tasks.pop();
            m_queuedMedia.clear();
        }
        m_cond.notify_all();
        m_thread.join();
    }
}

bool ThumbnailerWorker::generateThumbnail( Task task )
{
    assert( task.media->type() != Media::Type::Audio );

    const auto files = task.media->files();
    if ( files.empty() == true )
    {
        LOG_WARN( "Can't generate thumbnail for a media without associated files (",
                  task.media->title() );
        return false;
    }
    auto mainFileIt = std::find_if( files.cbegin(), files.cend(),
                                    [](FilePtr f) {
                                        return f->isMain();
                                   });
    if ( mainFileIt == files.cend() )
    {
        assert( !"A media must have a file of type Main" );
        return false;
    }
    auto file = std::static_pointer_cast<File>( *mainFileIt );
    std::string mrl;
    try
    {
        mrl = file->mrl();
    }
    catch ( const fs::errors::DeviceRemoved& )
    {
        LOG_WARN( "Aborting file ", file->rawMrl(), " generation due to its "
                  "containing device being missing" );
        return false;
    }

    auto m = static_cast<Media*>( task.media.get() );
    bool isFirstThumbnailGeneration = false;
    if ( m->thumbnailStatus( task.sizeType ) == ThumbnailStatus::Missing )
    {
        /*
         * Insert a failure record before computing the thumbnail.
         * If the thumbnailer crashes, we don't want to re-run it. If it succeeds,
         * the thumbnail will be updated right after
         * This is done here instead of from the mainloop as we don't want to prevent
         * the thumbnail generation of a file that has been removed.
         *
         * This assumes that the thumbnail won't crash if it succeeded once.
         */
        m->setThumbnail( std::make_shared<Thumbnail>( m_ml, ThumbnailStatus::Crash,
                                                      Thumbnail::Origin::Media,
                                                      task.sizeType ) );
        isFirstThumbnailGeneration = true;
    }
    auto thumbnail = m->thumbnail( task.sizeType );
    if ( thumbnail == nullptr )
    {
        // Handle sporadic read errors gracefully
        assert( !"The thumbnail can't be nullptr as it just was inserted" );
        return false;
    }
    auto dest = Thumbnail::path( m_ml, thumbnail->id() );
    LOG_DEBUG( "Generating ", mrl, " thumbnail in ", dest );

    if ( m_generator->generate( *m, mrl, task.desiredWidth, task.desiredHeight,
                                task.position, dest ) == false )
    {
        if ( m_run == false )
        {
            /*
             * The generation failed because the thumbnailer was interrupted.
             *
             * If we were trying to generate the first thumbnail for this media
             * we need to remove the record, as there were no crashes, and we
             * don't want to report that information to the user.
             * Otherwise, just keep the previous thumbnail.
             */
            if ( isFirstThumbnailGeneration == true )
                m->removeThumbnail( task.sizeType );
        }
        else
        {
            // Otherwise, ensure the status is "Failure" (since getting here means
            // there was no crash) and bump the number of attempt
            thumbnail->markFailed();
        }
        return false;
    }

    auto destMrl = utils::file::toMrl( dest );
    /*
     * Even if we had a thumbnail before, we still might need to update its
     * status, so we still invoke setThumbnail and let it decide what needs
     * to be updated in db
     */
    return m->setThumbnail( std::make_shared<Thumbnail>( m_ml, std::move( destMrl ),
                        Thumbnail::Origin::Media, task.sizeType, true ) );
}

}
<commit_msg>ThumbnailerWorker: Don't duplicate wait condition predicate<commit_after>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "ThumbnailerWorker.h"

#include "Media.h"
#include "File.h"
#include "logging/Logger.h"
#include "MediaLibrary.h"
#include "medialibrary/filesystem/Errors.h"
#include "utils/Filename.h"
#include "utils/File.h"
#include "medialibrary/IThumbnailer.h"

#include <algorithm>

namespace medialibrary
{

ThumbnailerWorker::ThumbnailerWorker( MediaLibraryPtr ml,
                                      std::shared_ptr<IThumbnailer> thumbnailer )
    : m_ml( ml )
    , m_run( false )
    , m_generator( std::move( thumbnailer ) )
    , m_paused( false )
{
}

ThumbnailerWorker::~ThumbnailerWorker()
{
    stop();
}

void ThumbnailerWorker::requestThumbnailInternal( int64_t mediaId, MediaPtr media,
                                                  ThumbnailSizeType sizeType,
                                                  uint32_t desiredWidth,
                                                  uint32_t desiredHeight,
                                                  float position )
{
    std::unique_lock<compat::Mutex> lock( m_mutex );

    if ( m_queuedMedia.find( mediaId ) != cend( m_queuedMedia ) )
        return;

    Task t{
        mediaId,
        std::move( media ),
        sizeType,
        desiredWidth,
        desiredHeight,
        position
    };
    m_queuedMedia.insert( mediaId );
    m_tasks.push( std::move( t ) );
    assert( m_tasks.size() == m_queuedMedia.size() );
    if ( m_thread.get_id() == compat::Thread::id{} )
    {
        m_run = true;
        m_thread = compat::Thread( &ThumbnailerWorker::run, this );
    }
    else
        m_cond.notify_all();
}

void ThumbnailerWorker::runCleanupRequests()
{
    auto requests = Thumbnail::fetchCleanups( m_ml );
    for ( const auto& r : requests )
    {
        auto path = m_ml->thumbnailPath() + r.second;
        LOG_DEBUG( "Running cleanup request #", r.first, ": removing ", path );
        if ( !utils::fs::remove( path ) )
        {
            try
            {
                utils::fs::fileSize( path );
                /*
                 * If we can compute the file size, the file still exists so we
                 * should try to delete it again later
                 */
                continue;
            }
            catch ( const medialibrary::fs::errors::Exception& )
            {
                /*
                 * Let's assume the file doesn't exist anymore so we can still
                 * remove the cleanup request.
                 */
            }
        }
        if ( Thumbnail::removeCleanupRequest( m_ml, r.first ) == false )
            LOG_WARN( "Failed to remove thumbnail cleanup request #", r.first );
    }
}

void ThumbnailerWorker::requestThumbnail( int64_t mediaId, ThumbnailSizeType sizeType,
                                          uint32_t desiredWidth, uint32_t desiredHeight,
                                          float position )
{
    requestThumbnailInternal( mediaId, nullptr, sizeType, desiredWidth, desiredHeight, position );
}

void ThumbnailerWorker::requestCleanupRun()
{
    requestThumbnailInternal( 0, nullptr, ThumbnailSizeType::Thumbnail,
                              0, 0, 0.f );
}

void ThumbnailerWorker::requestThumbnail( MediaPtr media, ThumbnailSizeType sizeType,
                                          uint32_t desiredWidth, uint32_t desiredHeight,
                                          float position )
{
    /* Call media->id() before moving media */
    int64_t mediaId = media->id();
    requestThumbnailInternal( mediaId, std::move(media), sizeType, desiredWidth, desiredHeight, position );
}

void ThumbnailerWorker::pause()
{
    std::lock_guard<compat::Mutex> lock( m_mutex );
    m_paused = true;
}

void ThumbnailerWorker::resume()
{
    std::lock_guard<compat::Mutex> lock( m_mutex );
    if ( m_paused == false )
        return;
    m_paused = false;
    m_cond.notify_all();
}

void ThumbnailerWorker::run()
{
    LOG_INFO( "Starting thumbnailer thread" );
    while ( m_run == true )
    {
        ML_UNHANDLED_EXCEPTION_INIT
        {
            Task t;
            {
                std::unique_lock<compat::Mutex> lock( m_mutex );
                m_cond.wait( lock, [this]() {
                    return ( m_tasks.size() > 0 && m_paused == false ) ||
                            m_run == false;
                });
                if ( m_run == false )
                    break;
                t = std::move( m_tasks.front() );
                m_tasks.pop();
                m_queuedMedia.erase( t.mediaId );
                assert( m_tasks.size() == m_queuedMedia.size() );
            }
            if ( t.mediaId == 0 )
                runCleanupRequests();
            if ( t.media == nullptr )
            {
                t.media = m_ml->media( t.mediaId );
                if ( t.media == nullptr )
                    /* No media found with this id */
                    continue;
            }
            bool res = generateThumbnail( t );
            m_ml->getCb()->onMediaThumbnailReady( t.media, t.sizeType, res );
        }
        ML_UNHANDLED_EXCEPTION_BODY( "ThumbnailerWorker" )
    }
    LOG_INFO( "Exiting thumbnailer thread" );
}

void ThumbnailerWorker::stop()
{
    bool running = true;
    if ( m_run.compare_exchange_strong( running, false ) )
    {
        m_generator->stop();
        {
            std::unique_lock<compat::Mutex> lock( m_mutex );
            while ( m_tasks.empty() == false )
                m_tasks.pop();
            m_queuedMedia.clear();
        }
        m_cond.notify_all();
        m_thread.join();
    }
}

bool ThumbnailerWorker::generateThumbnail( Task task )
{
    assert( task.media->type() != Media::Type::Audio );

    const auto files = task.media->files();
    if ( files.empty() == true )
    {
        LOG_WARN( "Can't generate thumbnail for a media without associated files (",
                  task.media->title() );
        return false;
    }
    auto mainFileIt = std::find_if( files.cbegin(), files.cend(),
                                    [](FilePtr f) {
                                        return f->isMain();
                                   });
    if ( mainFileIt == files.cend() )
    {
        assert( !"A media must have a file of type Main" );
        return false;
    }
    auto file = std::static_pointer_cast<File>( *mainFileIt );
    std::string mrl;
    try
    {
        mrl = file->mrl();
    }
    catch ( const fs::errors::DeviceRemoved& )
    {
        LOG_WARN( "Aborting file ", file->rawMrl(), " generation due to its "
                  "containing device being missing" );
        return false;
    }

    auto m = static_cast<Media*>( task.media.get() );
    bool isFirstThumbnailGeneration = false;
    if ( m->thumbnailStatus( task.sizeType ) == ThumbnailStatus::Missing )
    {
        /*
         * Insert a failure record before computing the thumbnail.
         * If the thumbnailer crashes, we don't want to re-run it. If it succeeds,
         * the thumbnail will be updated right after
         * This is done here instead of from the mainloop as we don't want to prevent
         * the thumbnail generation of a file that has been removed.
         *
         * This assumes that the thumbnail won't crash if it succeeded once.
         */
        m->setThumbnail( std::make_shared<Thumbnail>( m_ml, ThumbnailStatus::Crash,
                                                      Thumbnail::Origin::Media,
                                                      task.sizeType ) );
        isFirstThumbnailGeneration = true;
    }
    auto thumbnail = m->thumbnail( task.sizeType );
    if ( thumbnail == nullptr )
    {
        // Handle sporadic read errors gracefully
        assert( !"The thumbnail can't be nullptr as it just was inserted" );
        return false;
    }
    auto dest = Thumbnail::path( m_ml, thumbnail->id() );
    LOG_DEBUG( "Generating ", mrl, " thumbnail in ", dest );

    if ( m_generator->generate( *m, mrl, task.desiredWidth, task.desiredHeight,
                                task.position, dest ) == false )
    {
        if ( m_run == false )
        {
            /*
             * The generation failed because the thumbnailer was interrupted.
             *
             * If we were trying to generate the first thumbnail for this media
             * we need to remove the record, as there were no crashes, and we
             * don't want to report that information to the user.
             * Otherwise, just keep the previous thumbnail.
             */
            if ( isFirstThumbnailGeneration == true )
                m->removeThumbnail( task.sizeType );
        }
        else
        {
            // Otherwise, ensure the status is "Failure" (since getting here means
            // there was no crash) and bump the number of attempt
            thumbnail->markFailed();
        }
        return false;
    }

    auto destMrl = utils::file::toMrl( dest );
    /*
     * Even if we had a thumbnail before, we still might need to update its
     * status, so we still invoke setThumbnail and let it decide what needs
     * to be updated in db
     */
    return m->setThumbnail( std::make_shared<Thumbnail>( m_ml, std::move( destMrl ),
                        Thumbnail::Origin::Media, task.sizeType, true ) );
}

}
<|endoftext|>