<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author:  Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------

#include "IncrementalParser.h"
#include "ASTDumper.h"
#include "ASTNodeEraser.h"
#include "DeclCollector.h"
#include "DeclExtractor.h"
#include "DynamicLookup.h"
#include "ReturnSynthesizer.h"
#include "ValuePrinterSynthesizer.h"
#include "cling/Interpreter/CIFactory.h"
#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/Transaction.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclGroup.h"
#include "clang/Basic/FileManager.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Parse/Parser.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Serialization/ASTWriter.h"

#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_os_ostream.h"

#include <ctime>
#include <iostream>
#include <stdio.h>
#include <sstream>

using namespace clang;

namespace cling {
  IncrementalParser::IncrementalParser(Interpreter* interp,
                                       int argc, const char* const *argv,
                                       const char* llvmdir):
    m_Interpreter(interp), m_Consumer(0), m_FirstTransaction(0),
    m_LastTransaction(0) {

    CompilerInstance* CI
      = CIFactory::createCI(llvm::MemoryBuffer::getMemBuffer("", "CLING"),
                            argc, argv, llvmdir);
    assert(CI && "CompilerInstance is (null)!");

    m_Consumer = dyn_cast<DeclCollector>(&CI->getASTConsumer());
    assert(m_Consumer && "Expected ChainedConsumer!");

    m_CI.reset(CI);

    if (CI->getFrontendOpts().ProgramAction != clang::frontend::ParseSyntaxOnly){
      m_CodeGen.reset(CreateLLVMCodeGen(CI->getDiagnostics(), "cling input",
                                        CI->getCodeGenOpts(),
                                        *m_Interpreter->getLLVMContext()
                                        ));
    }

    CreateSLocOffsetGenerator();

    // Add transformers to the IncrementalParser, which owns them
    m_TTransformers.push_back(new EvaluateTSynthesizer(&CI->getSema()));

    m_TTransformers.push_back(new ValuePrinterSynthesizer(&CI->getSema(), 0));
    m_TTransformers.push_back(new ReturnSynthesizer(&CI->getSema()));
    m_TTransformers.push_back(new ASTDumper());
    m_TTransformers.push_back(new DeclExtractor(&getCI()->getSema()));

    m_Parser.reset(new Parser(CI->getPreprocessor(), CI->getSema(),
                              false /*skipFuncBodies*/));
    CI->getPreprocessor().EnterMainSourceFile();
    // Initialize the parser after we have entered the main source file.
    m_Parser->Initialize();
    // Perform initialization that occurs after the parser has been initialized
    // but before it parses anything. Initializes the consumers too.
    CI->getSema().Initialize();
  }

  IncrementalParser::~IncrementalParser() {
     if (hasCodeGenerator()) {
       getCodeGenerator()->ReleaseModule();
     }
     const Transaction* T = getFirstTransaction();
     const Transaction* nextT = 0;
     while (T) {
       nextT = T->getNext();
       delete T;
       T = nextT;
     }

     for (size_t i = 0; i < m_TTransformers.size(); ++i)
       delete m_TTransformers[i];
  }

  void IncrementalParser::beginTransaction(const CompilationOptions& Opts) {
    llvm::Module* M = 0;
    if (hasCodeGenerator())
      M = getCodeGenerator()->GetModule();

    Transaction* NewCurT = new Transaction(Opts, M);
    Transaction* OldCurT = m_Consumer->getTransaction();
    m_Consumer->setTransaction(NewCurT);
    // If we are in the middle of transaction and we see another begin
    // transaction - it must be nested transaction.
    if (OldCurT && !OldCurT->isCompleted()) {
      OldCurT->addNestedTransaction(NewCurT); // takes the ownership
      return;
    }

    if (!m_FirstTransaction) {
      m_FirstTransaction = NewCurT;
      m_LastTransaction = NewCurT;
    }
    else {
      m_LastTransaction->setNext(NewCurT);
      m_LastTransaction = NewCurT;
    }
  }

  void IncrementalParser::endTransaction() const {
    Transaction* CurT = m_Consumer->getTransaction();
    CurT->setCompleted();
    const DiagnosticsEngine& Diags = getCI()->getSema().getDiagnostics();

    //TODO: Make the enum orable.
    if (Diags.getNumWarnings() > 0)
      CurT->setIssuedDiags(Transaction::kWarnings);

    if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
      CurT->setIssuedDiags(Transaction::kErrors);

    if (CurT->isNestedTransaction()) {
      assert(!CurT->getParent()->isCompleted()
             && "Parent transaction completed!?");
      // FIXME: Not sure what I meant :) REVISIT
      //CurT = m_Consumer->getTransaction()->getParent();
    }
  }

  void IncrementalParser::commitCurrentTransaction() {
    Transaction* CurT = m_Consumer->getTransaction();
    assert(CurT->isCompleted() && "Transaction not ended!?");

    // Check for errors coming from our custom consumers.
    DiagnosticConsumer& DClient = m_CI->getDiagnosticClient();

    // Check for errors...
    if (CurT->getIssuedDiags() == Transaction::kErrors) {
      rollbackTransaction(CurT);
      DClient.EndSourceFile();
      return;
    }

    // We are sure it's safe to pipe it through the transformers
    bool success = true;
    for (size_t i = 0; i < m_TTransformers.size(); ++i) {
      DClient.BeginSourceFile(getCI()->getLangOpts(),
                              &getCI()->getPreprocessor());
      success = m_TTransformers[i]->TransformTransaction(*CurT);
      DClient.EndSourceFile();
      if (!success) {
        break;
      }
    }

    m_CI->getDiagnostics().Reset(); // FIXME: Should be in rollback transaction.

    if (!success) {
      // Roll back on error in a transformer
      rollbackTransaction(CurT);
      DClient.EndSourceFile();
      return;
    }

    // Pull all template instantiations in that came from the consumers.
    getCI()->getSema().PerformPendingInstantiations();
    DClient.EndSourceFile();

    m_Consumer->HandleTranslationUnit(getCI()->getASTContext());

    if (CurT->getCompilationOpts().CodeGeneration && hasCodeGenerator()) {
      // Reset the module builder to clean up global initializers, c'tors, d'tors
      getCodeGenerator()->Initialize(getCI()->getASTContext());

      // codegen the transaction
      for (Transaction::const_iterator I = CurT->decls_begin(),
             E = CurT->decls_end(); I != E; ++I) {
        getCodeGenerator()->HandleTopLevelDecl(*I);
      }
      getCodeGenerator()->HandleTranslationUnit(getCI()->getASTContext());
      // run the static initializers that came from codegenning
      m_Interpreter->runStaticInitializersOnce();
    }

    CurT->setState(Transaction::kCommitted);
    InterpreterCallbacks* callbacks = m_Interpreter->getCallbacks();

    if (callbacks)
      callbacks->TransactionCommitted(*CurT);
  }

  void IncrementalParser::rollbackTransaction(Transaction* T) const {
    ASTNodeEraser NodeEraser(&getCI()->getSema());

    if (NodeEraser.RevertTransaction(T))
      T->setState(Transaction::kRolledBack);
    else
      T->setState(Transaction::kRolledBackWithErrors);
  }

  // Each input line is contained in separate memory buffer. The SourceManager
  // assigns sort-of invalid FileID for each buffer, i.e there is no FileEntry
  // for the MemoryBuffer's FileID. That in turn is problem because invalid
  // SourceLocations are given to the diagnostics. Thus the diagnostics cannot
  // order the overloads, for example
  //
  // Our work-around is creating a virtual file, which doesn't exist on the disk
  // with enormous size (no allocation is done). That file has valid FileEntry
  // and so on... We use it for generating valid SourceLocations with valid
  // offsets so that it doesn't cause any troubles to the diagnostics.
  //
  // +---------------------+
  // | Main memory buffer  |
  // +---------------------+
  // |  Virtual file SLoc  |
  // |    address space    |<-----------------+
  // |         ...         |<------------+    |
  // |         ...         |             |    |
  // |         ...         |<----+       |    |
  // |         ...         |     |       |    |
  // +~~~~~~~~~~~~~~~~~~~~~+     |       |    |
  // |     input_line_1    | ....+.......+..--+
  // +---------------------+     |       |
  // |     input_line_2    | ....+.....--+
  // +---------------------+     |
  // |          ...        |     |
  // +---------------------+     |
  // |     input_line_N    | ..--+
  // +---------------------+
  //
  void IncrementalParser::CreateSLocOffsetGenerator() {
    SourceManager& SM = getCI()->getSourceManager();
    FileManager& FM = SM.getFileManager();
    const FileEntry* FE
      = FM.getVirtualFile("InteractiveInputLineIncluder.h", 1U << 15U, time(0));
    m_VirtualFileID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User);

    assert(!m_VirtualFileID.isInvalid() && "No VirtualFileID created?");
  }

  IncrementalParser::EParseResult
  IncrementalParser::Compile(llvm::StringRef input,
                             const CompilationOptions& Opts) {

    beginTransaction(Opts);
    EParseResult Result = ParseInternal(input);
    endTransaction();

    commitCurrentTransaction();

    return Result;
  }

  Transaction* IncrementalParser::Parse(llvm::StringRef input,
                                        const CompilationOptions& Opts) {
    beginTransaction(Opts);
    ParseInternal(input);
    endTransaction();

    return getLastTransaction();
  }

  // Add the input to the memory buffer, parse it, and add it to the AST.
  IncrementalParser::EParseResult
  IncrementalParser::ParseInternal(llvm::StringRef input) {
    if (input.empty()) return IncrementalParser::kSuccess;

    PrettyStackTraceParserEntry CrashInfo(*m_Parser.get());

    // Recover resources if we crash before exiting this method.
    llvm::CrashRecoveryContextCleanupRegistrar<Parser>
      CleanupParser(m_Parser.get());

    Sema& S = getCI()->getSema();
    // Recover resources if we crash before exiting this method.
    llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S);

    Preprocessor& PP = m_CI->getPreprocessor();
    DiagnosticConsumer& DClient = m_CI->getDiagnosticClient();

    if (!PP.getCurrentLexer()) {
       PP.EnterSourceFile(m_CI->getSourceManager().getMainFileID(),
                          0, SourceLocation());
    }
    assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?");
    PP.enableIncrementalProcessing();

    DClient.BeginSourceFile(m_CI->getLangOpts(), &PP);

    std::ostringstream source_name;
    source_name << "input_line_" << (m_MemoryBuffers.size() + 1);

    // Create an uninitialized memory buffer, copy code in and append "\n"
    size_t InputSize = input.size(); // don't include trailing 0
    // MemBuffer size should *not* include terminating zero
    llvm::MemoryBuffer* MB
      = llvm::MemoryBuffer::getNewUninitMemBuffer(InputSize + 1,
                                                  source_name.str());
    char* MBStart = const_cast<char*>(MB->getBufferStart());
    memcpy(MBStart, input.data(), InputSize);
    memcpy(MBStart + InputSize, "\n", 2);

    m_MemoryBuffers.push_back(MB);
    SourceManager& SM = getCI()->getSourceManager();

    // Create SourceLocation, which will allow clang to order the overload
    // candidates for example
    SourceLocation NewLoc = SM.getLocForStartOfFile(m_VirtualFileID);
    NewLoc = NewLoc.getLocWithOffset(m_MemoryBuffers.size() + 1);

    // Create FileID for the current buffer
    FileID FID = SM.createFileIDForMemBuffer(m_MemoryBuffers.back(),
                                             /*LoadedID*/0,
                                             /*LoadedOffset*/0, NewLoc);

    PP.EnterSourceFile(FID, /*DirLookup*/0, NewLoc);

    Parser::DeclGroupPtrTy ADecl;

    while (!m_Parser->ParseTopLevelDecl(ADecl)) {
      // If we got a null return and something *was* parsed, ignore it.  This
      // is due to a top-level semicolon, an action override, or a parse error
      // skipping something.
      if (ADecl)
        m_Consumer->HandleTopLevelDecl(ADecl.getAsVal<DeclGroupRef>());
    };

    // Process any TopLevelDecls generated by #pragma weak.
    for (llvm::SmallVector<Decl*,2>::iterator I = S.WeakTopLevelDecls().begin(),
           E = S.WeakTopLevelDecls().end(); I != E; ++I) {
      m_Consumer->HandleTopLevelDecl(DeclGroupRef(*I));
    }

    S.PerformPendingInstantiations();

    DiagnosticsEngine& Diag = S.getDiagnostics();
    if (Diag.hasErrorOccurred())
      return IncrementalParser::kFailed;
    else if (Diag.getNumWarnings())
      return IncrementalParser::kSuccessWithWarnings;

    return IncrementalParser::kSuccess;
  }

  void IncrementalParser::unloadTransaction(Transaction* T) {
    if (!T)
      T = getLastTransaction();

    assert(T->getState() == Transaction::kCommitted &&
           "Unloading not commited transaction?");
    assert(T->getModule() &&
           "Trying to uncodegen transaction taken in syntax only mode. ");

    ASTNodeEraser NodeEraser(&getCI()->getSema());
    NodeEraser.RevertTransaction(T);

    InterpreterCallbacks* callbacks = m_Interpreter->getCallbacks();
    if (callbacks)
      callbacks->TransactionUnloaded(*T);
  }

} // namespace cling
<commit_msg>Add some support for nested transactions, needed by the upcoming patch of the  autoloading. There we need to subparse some content that naturally should end up as nested transaction.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author:  Axel Naumann <axel@cern.ch>
//------------------------------------------------------------------------------

#include "IncrementalParser.h"
#include "ASTDumper.h"
#include "ASTNodeEraser.h"
#include "DeclCollector.h"
#include "DeclExtractor.h"
#include "DynamicLookup.h"
#include "ReturnSynthesizer.h"
#include "ValuePrinterSynthesizer.h"
#include "cling/Interpreter/CIFactory.h"
#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/Transaction.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclGroup.h"
#include "clang/Basic/FileManager.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Parse/Parser.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Serialization/ASTWriter.h"

#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_os_ostream.h"

#include <ctime>
#include <iostream>
#include <stdio.h>
#include <sstream>

using namespace clang;

namespace cling {
  IncrementalParser::IncrementalParser(Interpreter* interp,
                                       int argc, const char* const *argv,
                                       const char* llvmdir):
    m_Interpreter(interp), m_Consumer(0), m_FirstTransaction(0),
    m_LastTransaction(0) {

    CompilerInstance* CI
      = CIFactory::createCI(llvm::MemoryBuffer::getMemBuffer("", "CLING"),
                            argc, argv, llvmdir);
    assert(CI && "CompilerInstance is (null)!");

    m_Consumer = dyn_cast<DeclCollector>(&CI->getASTConsumer());
    assert(m_Consumer && "Expected ChainedConsumer!");

    m_CI.reset(CI);

    if (CI->getFrontendOpts().ProgramAction != clang::frontend::ParseSyntaxOnly){
      m_CodeGen.reset(CreateLLVMCodeGen(CI->getDiagnostics(), "cling input",
                                        CI->getCodeGenOpts(),
                                        *m_Interpreter->getLLVMContext()
                                        ));
    }

    CreateSLocOffsetGenerator();

    // Add transformers to the IncrementalParser, which owns them
    m_TTransformers.push_back(new EvaluateTSynthesizer(&CI->getSema()));

    m_TTransformers.push_back(new ValuePrinterSynthesizer(&CI->getSema(), 0));
    m_TTransformers.push_back(new ReturnSynthesizer(&CI->getSema()));
    m_TTransformers.push_back(new ASTDumper());
    m_TTransformers.push_back(new DeclExtractor(&getCI()->getSema()));

    m_Parser.reset(new Parser(CI->getPreprocessor(), CI->getSema(),
                              false /*skipFuncBodies*/));
    CI->getPreprocessor().EnterMainSourceFile();
    // Initialize the parser after we have entered the main source file.
    m_Parser->Initialize();
    // Perform initialization that occurs after the parser has been initialized
    // but before it parses anything. Initializes the consumers too.
    CI->getSema().Initialize();
  }

  IncrementalParser::~IncrementalParser() {
     if (hasCodeGenerator()) {
       getCodeGenerator()->ReleaseModule();
     }
     const Transaction* T = getFirstTransaction();
     const Transaction* nextT = 0;
     while (T) {
       nextT = T->getNext();
       delete T;
       T = nextT;
     }

     for (size_t i = 0; i < m_TTransformers.size(); ++i)
       delete m_TTransformers[i];
  }

  void IncrementalParser::beginTransaction(const CompilationOptions& Opts) {
    llvm::Module* M = 0;
    if (hasCodeGenerator())
      M = getCodeGenerator()->GetModule();

    Transaction* NewCurT = new Transaction(Opts, M);
    Transaction* OldCurT = m_Consumer->getTransaction();
    m_Consumer->setTransaction(NewCurT);
    // If we are in the middle of transaction and we see another begin
    // transaction - it must be nested transaction.
    if (OldCurT && !OldCurT->isCompleted()) {
      OldCurT->addNestedTransaction(NewCurT); // takes the ownership
      return;
    }

    if (!m_FirstTransaction) {
      m_FirstTransaction = NewCurT;
      m_LastTransaction = NewCurT;
    }
    else {
      m_LastTransaction->setNext(NewCurT);
      m_LastTransaction = NewCurT;
    }
  }

  void IncrementalParser::endTransaction() const {
    Transaction* CurT = m_Consumer->getTransaction();
    CurT->setCompleted();
    const DiagnosticsEngine& Diags = getCI()->getSema().getDiagnostics();

    //TODO: Make the enum orable.
    if (Diags.getNumWarnings() > 0)
      CurT->setIssuedDiags(Transaction::kWarnings);

    if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
      CurT->setIssuedDiags(Transaction::kErrors);

    if (CurT->hasNestedTransactions()) {
      for(Transaction::const_nested_iterator I = CurT->nested_decls_begin(),
            E = CurT->nested_decls_end(); I != E; ++I)
        assert(!(*I)->isCompleted() && "Parent transaction completed!?");
    }

    if (CurT->isNestedTransaction()) {
      // TODO: Add proper logic in the case where there are multiple nested
      // transaction. This now won't handle the case where there are more than
      // one level 1 nested transactions.
      m_Consumer->setTransaction(CurT->getParent());
    }
  }

  void IncrementalParser::commitCurrentTransaction() {
    Transaction* CurT = m_Consumer->getTransaction();
    assert(CurT->isCompleted() && "Transaction not ended!?");

    // Check for errors coming from our custom consumers.
    DiagnosticConsumer& DClient = m_CI->getDiagnosticClient();

    // Check for errors...
    if (CurT->getIssuedDiags() == Transaction::kErrors) {
      rollbackTransaction(CurT);
      DClient.EndSourceFile();
      return;
    }

    // We are sure it's safe to pipe it through the transformers
    bool success = true;
    for (size_t i = 0; i < m_TTransformers.size(); ++i) {
      DClient.BeginSourceFile(getCI()->getLangOpts(),
                              &getCI()->getPreprocessor());
      success = m_TTransformers[i]->TransformTransaction(*CurT);
      DClient.EndSourceFile();
      if (!success) {
        break;
      }
    }

    m_CI->getDiagnostics().Reset(); // FIXME: Should be in rollback transaction.

    if (!success) {
      // Roll back on error in a transformer
      rollbackTransaction(CurT);
      DClient.EndSourceFile();
      return;
    }

    // Pull all template instantiations in that came from the consumers.
    getCI()->getSema().PerformPendingInstantiations();
    DClient.EndSourceFile();

    m_Consumer->HandleTranslationUnit(getCI()->getASTContext());

    if (CurT->getCompilationOpts().CodeGeneration && hasCodeGenerator()) {
      // Reset the module builder to clean up global initializers, c'tors, d'tors
      getCodeGenerator()->Initialize(getCI()->getASTContext());

      // codegen the transaction
      for (Transaction::const_iterator I = CurT->decls_begin(),
             E = CurT->decls_end(); I != E; ++I) {
        getCodeGenerator()->HandleTopLevelDecl(*I);
      }
      getCodeGenerator()->HandleTranslationUnit(getCI()->getASTContext());
      // run the static initializers that came from codegenning
      m_Interpreter->runStaticInitializersOnce();
    }

    CurT->setState(Transaction::kCommitted);
    InterpreterCallbacks* callbacks = m_Interpreter->getCallbacks();

    if (callbacks)
      callbacks->TransactionCommitted(*CurT);
  }

  void IncrementalParser::rollbackTransaction(Transaction* T) const {
    ASTNodeEraser NodeEraser(&getCI()->getSema());

    if (NodeEraser.RevertTransaction(T))
      T->setState(Transaction::kRolledBack);
    else
      T->setState(Transaction::kRolledBackWithErrors);
  }

  // Each input line is contained in separate memory buffer. The SourceManager
  // assigns sort-of invalid FileID for each buffer, i.e there is no FileEntry
  // for the MemoryBuffer's FileID. That in turn is problem because invalid
  // SourceLocations are given to the diagnostics. Thus the diagnostics cannot
  // order the overloads, for example
  //
  // Our work-around is creating a virtual file, which doesn't exist on the disk
  // with enormous size (no allocation is done). That file has valid FileEntry
  // and so on... We use it for generating valid SourceLocations with valid
  // offsets so that it doesn't cause any troubles to the diagnostics.
  //
  // +---------------------+
  // | Main memory buffer  |
  // +---------------------+
  // |  Virtual file SLoc  |
  // |    address space    |<-----------------+
  // |         ...         |<------------+    |
  // |         ...         |             |    |
  // |         ...         |<----+       |    |
  // |         ...         |     |       |    |
  // +~~~~~~~~~~~~~~~~~~~~~+     |       |    |
  // |     input_line_1    | ....+.......+..--+
  // +---------------------+     |       |
  // |     input_line_2    | ....+.....--+
  // +---------------------+     |
  // |          ...        |     |
  // +---------------------+     |
  // |     input_line_N    | ..--+
  // +---------------------+
  //
  void IncrementalParser::CreateSLocOffsetGenerator() {
    SourceManager& SM = getCI()->getSourceManager();
    FileManager& FM = SM.getFileManager();
    const FileEntry* FE
      = FM.getVirtualFile("InteractiveInputLineIncluder.h", 1U << 15U, time(0));
    m_VirtualFileID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User);

    assert(!m_VirtualFileID.isInvalid() && "No VirtualFileID created?");
  }

  IncrementalParser::EParseResult
  IncrementalParser::Compile(llvm::StringRef input,
                             const CompilationOptions& Opts) {

    beginTransaction(Opts);
    EParseResult Result = ParseInternal(input);
    endTransaction();

    commitCurrentTransaction();

    return Result;
  }

  Transaction* IncrementalParser::Parse(llvm::StringRef input,
                                        const CompilationOptions& Opts) {
    beginTransaction(Opts);
    ParseInternal(input);
    endTransaction();

    return getLastTransaction();
  }

  // Add the input to the memory buffer, parse it, and add it to the AST.
  IncrementalParser::EParseResult
  IncrementalParser::ParseInternal(llvm::StringRef input) {
    if (input.empty()) return IncrementalParser::kSuccess;

    PrettyStackTraceParserEntry CrashInfo(*m_Parser.get());

    // Recover resources if we crash before exiting this method.
    llvm::CrashRecoveryContextCleanupRegistrar<Parser>
      CleanupParser(m_Parser.get());

    Sema& S = getCI()->getSema();
    // Recover resources if we crash before exiting this method.
    llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S);

    Preprocessor& PP = m_CI->getPreprocessor();
    DiagnosticConsumer& DClient = m_CI->getDiagnosticClient();

    if (!PP.getCurrentLexer()) {
       PP.EnterSourceFile(m_CI->getSourceManager().getMainFileID(),
                          0, SourceLocation());
    }
    assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?");
    PP.enableIncrementalProcessing();

    DClient.BeginSourceFile(m_CI->getLangOpts(), &PP);

    std::ostringstream source_name;
    source_name << "input_line_" << (m_MemoryBuffers.size() + 1);

    // Create an uninitialized memory buffer, copy code in and append "\n"
    size_t InputSize = input.size(); // don't include trailing 0
    // MemBuffer size should *not* include terminating zero
    llvm::MemoryBuffer* MB
      = llvm::MemoryBuffer::getNewUninitMemBuffer(InputSize + 1,
                                                  source_name.str());
    char* MBStart = const_cast<char*>(MB->getBufferStart());
    memcpy(MBStart, input.data(), InputSize);
    memcpy(MBStart + InputSize, "\n", 2);

    m_MemoryBuffers.push_back(MB);
    SourceManager& SM = getCI()->getSourceManager();

    // Create SourceLocation, which will allow clang to order the overload
    // candidates for example
    SourceLocation NewLoc = SM.getLocForStartOfFile(m_VirtualFileID);
    NewLoc = NewLoc.getLocWithOffset(m_MemoryBuffers.size() + 1);

    // Create FileID for the current buffer
    FileID FID = SM.createFileIDForMemBuffer(m_MemoryBuffers.back(),
                                             /*LoadedID*/0,
                                             /*LoadedOffset*/0, NewLoc);

    PP.EnterSourceFile(FID, /*DirLookup*/0, NewLoc);

    Parser::DeclGroupPtrTy ADecl;

    while (!m_Parser->ParseTopLevelDecl(ADecl)) {
      // If we got a null return and something *was* parsed, ignore it.  This
      // is due to a top-level semicolon, an action override, or a parse error
      // skipping something.
      if (ADecl)
        m_Consumer->HandleTopLevelDecl(ADecl.getAsVal<DeclGroupRef>());
    };

    // Process any TopLevelDecls generated by #pragma weak.
    for (llvm::SmallVector<Decl*,2>::iterator I = S.WeakTopLevelDecls().begin(),
           E = S.WeakTopLevelDecls().end(); I != E; ++I) {
      m_Consumer->HandleTopLevelDecl(DeclGroupRef(*I));
    }

    S.PerformPendingInstantiations();

    DiagnosticsEngine& Diag = S.getDiagnostics();
    if (Diag.hasErrorOccurred())
      return IncrementalParser::kFailed;
    else if (Diag.getNumWarnings())
      return IncrementalParser::kSuccessWithWarnings;

    return IncrementalParser::kSuccess;
  }

  void IncrementalParser::unloadTransaction(Transaction* T) {
    if (!T)
      T = getLastTransaction();

    assert(T->getState() == Transaction::kCommitted &&
           "Unloading not commited transaction?");
    assert(T->getModule() &&
           "Trying to uncodegen transaction taken in syntax only mode. ");

    ASTNodeEraser NodeEraser(&getCI()->getSema());
    NodeEraser.RevertTransaction(T);

    InterpreterCallbacks* callbacks = m_Interpreter->getCallbacks();
    if (callbacks)
      callbacks->TransactionUnloaded(*T);
  }

} // namespace cling
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

// RUN: cat %s | %cling 2>&1 | FileCheck %s

// This file should be used as regression test for the value printing subsystem
// Reproducers of fixed bugs should be put here

extern "C" int mustPrintFirst() { return 6; }
mustPrintFirst
// CHECK: (int (*)()) Function

// PR #93006
.rawInput 1
extern "C" int printf(const char* fmt, ...);
class A {
public:
  int Var;
  A(int arg) { Var = arg; }
  int someFunc(float) { return 42; }
  ~A() { printf("A d'tor\n"); }
};

const A& foo(const A& arg) { return arg; }
A foo2(const A& arg) { return A(42); }
.rawInput 0

foo(A(12)).Var
// CHECK: (const int) 12
// CHECK: A d'tor
// End PR #93006

// myvector.end() failed to print (roottest/cling/stl/default/VectorSort.C)
foo2(A(42))
// CHECK: (A) @0x{{[0-9a-f]+}}
// CHECK: A d'tor

 // Savannah #96523
int *p = (int*)0x123;
p // CHECK: (int *) 0x123
const int *q = (int*)0x123;
q // CHECK: (const int *) 0x123 <invalid memory address>

0.00001234L // CHECK-NEXT: (long double) 1.2340000e-05L

// PR ROOT-5467
&A::someFunc // CHECK: (int (A::*)(float)) Function @0x{{[0-9a-f]+}}

nullptr // CHECK: (nullptr_t) nullptr

true // CHECK: (bool) true
false // CHECK: (bool) false

unordered_multiset<float> {1} // ROOT-7310
// expected-error@2 {{use of undeclared identifier 'unordered_multiset'}}
// expected-error@2 {{expected '(' for function-style cast or type construction}}
// expected-error@2 {{initializer list cannot be used on the right hand side of operator '>'}}

#include <unordered_set>
std::unordered_multiset<float> {1}
// FIXME: BROKEN_ON_LINUX-CHECK: (std::unordered_multiset<float>) { 1.00000f }

// ROOT-7426
#include <string>
#include <vector>
std::vector<std::string> a = { "a", "b", "c" } // CHECK: (std::vector<std::string> &) { "a", "b", "c" }
a // CHECK: (std::vector<std::string> &) { "a", "b", "c" }
a[0] // CHECK: ({{.*}} &) "a"

// ROOT-7918
struct Enumer {
#ifndef _WIN32
  // This variant fails on Windows, and seems conforming behaviour to do so
  enum H {  h = (unsigned long long )-1 };
#else
  enum H : unsigned long long { h = (unsigned long long )-1 };
#endif
};
Enumer::h
// CHECK: (Enumer::H) (Enumer::H::h) : (unsigned long{{( long)?}}) 18446744073709551615

// ROOT-7837
auto bla=[](double *x, double *par, int blub){return x[0]*blub;} // CHECK: ((lambda) &) @0x

#include <functional>
using namespace std::placeholders;
auto fn_moo = std::bind (bla, _1,_2,10) // CHECK: ERROR in cling's callPrintValue(): missing value string.
// expected-error {{use of undeclared identifier 'lambda'}}
// expected-error {{expected expression}}
// expected-error {{type name requires a specifier or qualifier}}
// expected-error {{expected ')'}}
// expected-note  {{to match this '('}}

// Make sure cling survives
12 // CHECK: (int) 12

// ROOT-8077
.rawInput 1
#include <string>
void f(std::string) {}
.rawInput 0
f // CHECK: (void (*)(std::string)) Function @0x{{[0-9a-f]+}}
// CHECK: at input_line_{{[0-9].*}}:1:
// CHECK: void f(std::string) {}

class notapointer {};
struct OverloadedAddrOf {
  notapointer operator&() {
    return notapointer();
  }
};
OverloadedAddrOf overloadedAddrOf
// CHECK: (OverloadedAddrOf &) @0x{{[0-9a-f]+}}

namespace PR180 {
  class base {};

  template <class B>
  class Derived : public B {};
  using Foo = Derived<base>;
}
auto bar = PR180::Foo()
// CHECK: (PR180::Derived<PR180::base> &) @0x{{[0-9a-f]+}}
<commit_msg>cling can now print lamda-dependent types!<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

// RUN: cat %s | %cling 2>&1 | FileCheck %s

// This file should be used as regression test for the value printing subsystem
// Reproducers of fixed bugs should be put here

extern "C" int mustPrintFirst() { return 6; }
mustPrintFirst
// CHECK: (int (*)()) Function

// PR #93006
.rawInput 1
extern "C" int printf(const char* fmt, ...);
class A {
public:
  int Var;
  A(int arg) { Var = arg; }
  int someFunc(float) { return 42; }
  ~A() { printf("A d'tor\n"); }
};

const A& foo(const A& arg) { return arg; }
A foo2(const A& arg) { return A(42); }
.rawInput 0

foo(A(12)).Var
// CHECK: (const int) 12
// CHECK: A d'tor
// End PR #93006

// myvector.end() failed to print (roottest/cling/stl/default/VectorSort.C)
foo2(A(42))
// CHECK: (A) @0x{{[0-9a-f]+}}
// CHECK: A d'tor

 // Savannah #96523
int *p = (int*)0x123;
p // CHECK: (int *) 0x123
const int *q = (int*)0x123;
q // CHECK: (const int *) 0x123 <invalid memory address>

0.00001234L // CHECK-NEXT: (long double) 1.2340000e-05L

// PR ROOT-5467
&A::someFunc // CHECK: (int (A::*)(float)) Function @0x{{[0-9a-f]+}}

nullptr // CHECK: (nullptr_t) nullptr

true // CHECK: (bool) true
false // CHECK: (bool) false

unordered_multiset<float> {1} // ROOT-7310
// expected-error@2 {{use of undeclared identifier 'unordered_multiset'}}
// expected-error@2 {{expected '(' for function-style cast or type construction}}
// expected-error@2 {{initializer list cannot be used on the right hand side of operator '>'}}

#include <unordered_set>
std::unordered_multiset<float> {1}
// FIXME: BROKEN_ON_LINUX-CHECK: (std::unordered_multiset<float>) { 1.00000f }

// ROOT-7426
#include <string>
#include <vector>
std::vector<std::string> a = { "a", "b", "c" } // CHECK: (std::vector<std::string> &) { "a", "b", "c" }
a // CHECK: (std::vector<std::string> &) { "a", "b", "c" }
a[0] // CHECK: ({{.*}} &) "a"

// ROOT-7918
struct Enumer {
#ifndef _WIN32
  // This variant fails on Windows, and seems conforming behaviour to do so
  enum H {  h = (unsigned long long )-1 };
#else
  enum H : unsigned long long { h = (unsigned long long )-1 };
#endif
};
Enumer::h
// CHECK: (Enumer::H) (Enumer::H::h) : (unsigned long{{( long)?}}) 18446744073709551615

// ROOT-7837
auto bla=[](double *x, double *par, int blub){return x[0]*blub;} // CHECK: ((lambda) &) @0x{{[0-9a-f]+}}

#include <functional>
using namespace std::placeholders;
auto fn_moo = std::bind (bla, _1,_2,10) // CHECK: ({{.*\(lambda\).*}}> &) @0x{{[0-9a-f]+}}

// Make sure cling survives
12 // CHECK: (int) 12

// ROOT-8077
.rawInput 1
#include <string>
void f(std::string) {}
.rawInput 0
f // CHECK: (void (*)(std::string)) Function @0x{{[0-9a-f]+}}
// CHECK: at input_line_{{[0-9].*}}:1:
// CHECK: void f(std::string) {}

class notapointer {};
struct OverloadedAddrOf {
  notapointer operator&() {
    return notapointer();
  }
};
OverloadedAddrOf overloadedAddrOf
// CHECK: (OverloadedAddrOf &) @0x{{[0-9a-f]+}}

namespace PR180 {
  class base {};

  template <class B>
  class Derived : public B {};
  using Foo = Derived<base>;
}
auto bar = PR180::Foo()
// CHECK: (PR180::Derived<PR180::base> &) @0x{{[0-9a-f]+}}
<|endoftext|>


<commit_before>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "chainparams.h"
#include "clientversion.h"
#include "rpcserver.h"
#include "init.h"
#include "noui.h"
#include "scheduler.h"
#include "util.h"
#include "httpserver.h"
#include "httprpc.h"
#include "rpcserver.h"

#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>

#include <stdio.h>

/* Introduction text for doxygen: */

/*! \mainpage Developer documentation
 *
 * \section intro_sec Introduction
 *
 * This is the developer documentation of the reference client for an experimental new digital currency called Bitcoin (https://www.bitcoin.org/),
 * which enables instant payments to anyone, anywhere in the world. Bitcoin uses peer-to-peer technology to operate
 * with no central authority: managing transactions and issuing money are carried out collectively by the network.
 *
 * The software is a community-driven open source project, released under the MIT license.
 *
 * \section Navigation
 * Use the buttons Namespaces, Classes or Files at the top of the page to start navigating the code.
 */

static bool fDaemon;

void WaitForShutdown(boost::thread_group* threadGroup)
{
    bool fShutdown = ShutdownRequested();
    // Tell the main threads to shutdown.
    while (!fShutdown)
    {
        MilliSleep(200);
        fShutdown = ShutdownRequested();
    }
    if (threadGroup)
    {
        Interrupt(*threadGroup);
        threadGroup->join_all();
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// Start
//
bool AppInit(int argc, char* argv[])
{
    boost::thread_group threadGroup;
    CScheduler scheduler;

    bool fRet = false;

    //
    // Parameters
    //
    // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
    ParseParameters(argc, argv);

    // Process help and version before taking care about datadir
    if (mapArgs.count("-?") || mapArgs.count("-h") ||  mapArgs.count("-help") || mapArgs.count("-version"))
    {
        std::string strUsage = _("Bitcoin Core Daemon") + " " + _("version") + " " + FormatFullVersion() + "\n";

        if (mapArgs.count("-version"))
        {
            strUsage += LicenseInfo();
        }
        else
        {
            strUsage += "\n" + _("Usage:") + "\n" +
                  "  bitcoind [options]                     " + _("Start Bitcoin Core Daemon") + "\n";

            strUsage += "\n" + HelpMessage(HMM_BITCOIND);
        }

        fprintf(stdout, "%s", strUsage.c_str());
        return false;
    }

    try
    {
        if (!boost::filesystem::is_directory(GetDataDir(false)))
        {
            fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
            return false;
        }
        try
        {
            ReadConfigFile(mapArgs, mapMultiArgs);
        } catch (const std::exception& e) {
            fprintf(stderr,"Error reading configuration file: %s\n", e.what());
            return false;
        }
        // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
        try {
            SelectParams(ChainNameFromCommandLine());
        } catch (const std::exception& e) {
            fprintf(stderr, "Error: %s\n", e.what());
            return false;
        }

        // Command-line RPC
        bool fCommandLine = false;
        for (int i = 1; i < argc; i++)
            if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "bitcoin:"))
                fCommandLine = true;

        if (fCommandLine)
        {
            fprintf(stderr, "Error: There is no RPC client functionality in bitcoind anymore. Use the bitcoin-cli utility instead.\n");
            exit(1);
        }
#ifndef WIN32
        fDaemon = GetBoolArg("-daemon", false);
        if (fDaemon)
        {
            fprintf(stdout, "Bitcoin server starting\n");

            // Daemonize
            pid_t pid = fork();
            if (pid < 0)
            {
                fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
                return false;
            }
            if (pid > 0) // Parent process, pid is child process id
            {
                return true;
            }
            // Child process falls through to rest of initialization

            pid_t sid = setsid();
            if (sid < 0)
                fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
        }
#endif
        SoftSetBoolArg("-server", true);

        // Set this early so that parameter interactions go to console
        InitLogging();
        InitParameterInteraction();
        fRet = AppInit2(threadGroup, scheduler);
    }
    catch (const std::exception& e) {
        PrintExceptionContinue(&e, "AppInit()");
    } catch (...) {
        PrintExceptionContinue(NULL, "AppInit()");
    }

    if (!fRet)
    {
        Interrupt(threadGroup);
        // threadGroup.join_all(); was left out intentionally here, because we didn't re-test all of
        // the startup-failure cases to make sure they don't result in a hang due to some
        // thread-blocking-waiting-for-another-thread-during-startup case
    } else {
        WaitForShutdown(&threadGroup);
    }
    Shutdown();

    return fRet;
}

int main(int argc, char* argv[])
{
    SetupEnvironment();

    // Connect bitcoind signal handlers
    noui_connect();

    return (AppInit(argc, argv) ? 0 : 1);
}
<commit_msg>bitcoind client identification (MVHF-CORE-SW-REQ-11-1 / MVHF-CORE-DES-IDME-1)<commit_after>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2015 The Bitcoin Core developers
// Copyright (c) 2016 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "chainparams.h"
#include "clientversion.h"
#include "rpcserver.h"
#include "init.h"
#include "noui.h"
#include "scheduler.h"
#include "util.h"
#include "httpserver.h"
#include "httprpc.h"
#include "rpcserver.h"

#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>

#include <stdio.h>

/* Introduction text for doxygen: */

/*! \mainpage Developer documentation
 *
 * \section intro_sec Introduction
 *
 * This is the developer documentation of the reference client for an experimental new digital currency called Bitcoin (https://www.bitcoin.org/),
 * which enables instant payments to anyone, anywhere in the world. Bitcoin uses peer-to-peer technology to operate
 * with no central authority: managing transactions and issuing money are carried out collectively by the network.
 *
 * The software is a community-driven open source project, released under the MIT license.
 *
 * \section Navigation
 * Use the buttons Namespaces, Classes or Files at the top of the page to start navigating the code.
 */

static bool fDaemon;

void WaitForShutdown(boost::thread_group* threadGroup)
{
    bool fShutdown = ShutdownRequested();
    // Tell the main threads to shutdown.
    while (!fShutdown)
    {
        MilliSleep(200);
        fShutdown = ShutdownRequested();
    }
    if (threadGroup)
    {
        Interrupt(*threadGroup);
        threadGroup->join_all();
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// Start
//
bool AppInit(int argc, char* argv[])
{
    boost::thread_group threadGroup;
    CScheduler scheduler;

    bool fRet = false;

    //
    // Parameters
    //
    // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
    ParseParameters(argc, argv);

    // Process help and version before taking care about datadir
    if (mapArgs.count("-?") || mapArgs.count("-h") ||  mapArgs.count("-help") || mapArgs.count("-version"))
    {
        std::string strUsage = _("Bitcoin MVF-Core Daemon") + " " + _("version") + " " + FormatFullVersion() + "\n";  // MVF-Core client name (MVHF-CORE-DES-IDME-1)

        if (mapArgs.count("-version"))
        {
            strUsage += LicenseInfo();
        }
        else
        {
            strUsage += "\n" + _("Usage:") + "\n" +
                  "  bitcoind [options]                     " + _("Start Bitcoin MVF-Core Daemon") + "\n";  // MVF-Core client name (MVHF-CORE-DES-IDME-1)

            strUsage += "\n" + HelpMessage(HMM_BITCOIND);
        }

        fprintf(stdout, "%s", strUsage.c_str());
        return false;
    }

    try
    {
        if (!boost::filesystem::is_directory(GetDataDir(false)))
        {
            fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
            return false;
        }
        try
        {
            ReadConfigFile(mapArgs, mapMultiArgs);
        } catch (const std::exception& e) {
            fprintf(stderr,"Error reading configuration file: %s\n", e.what());
            return false;
        }
        // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
        try {
            SelectParams(ChainNameFromCommandLine());
        } catch (const std::exception& e) {
            fprintf(stderr, "Error: %s\n", e.what());
            return false;
        }

        // Command-line RPC
        bool fCommandLine = false;
        for (int i = 1; i < argc; i++)
            if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "bitcoin:"))
                fCommandLine = true;

        if (fCommandLine)
        {
            fprintf(stderr, "Error: There is no RPC client functionality in bitcoind anymore. Use the bitcoin-cli utility instead.\n");
            exit(1);
        }
#ifndef WIN32
        fDaemon = GetBoolArg("-daemon", false);
        if (fDaemon)
        {
            fprintf(stdout, "Bitcoin server starting\n");

            // Daemonize
            pid_t pid = fork();
            if (pid < 0)
            {
                fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
                return false;
            }
            if (pid > 0) // Parent process, pid is child process id
            {
                return true;
            }
            // Child process falls through to rest of initialization

            pid_t sid = setsid();
            if (sid < 0)
                fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
        }
#endif
        SoftSetBoolArg("-server", true);

        // Set this early so that parameter interactions go to console
        InitLogging();
        InitParameterInteraction();
        fRet = AppInit2(threadGroup, scheduler);
    }
    catch (const std::exception& e) {
        PrintExceptionContinue(&e, "AppInit()");
    } catch (...) {
        PrintExceptionContinue(NULL, "AppInit()");
    }

    if (!fRet)
    {
        Interrupt(threadGroup);
        // threadGroup.join_all(); was left out intentionally here, because we didn't re-test all of
        // the startup-failure cases to make sure they don't result in a hang due to some
        // thread-blocking-waiting-for-another-thread-during-startup case
    } else {
        WaitForShutdown(&threadGroup);
    }
    Shutdown();

    return fRet;
}

int main(int argc, char* argv[])
{
    SetupEnvironment();

    // Connect bitcoind signal handlers
    noui_connect();

    return (AppInit(argc, argv) ? 0 : 1);
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 NoBNC
 * Copyright (C) 2004-2015 ZNC, see the NOTICE file for details.
 *
 * 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.
 */

#include "pidfile.h"
#include <nobnc/noapp_p.h>
#include <nobnc/nomodulecall.h>
#include <nobnc/noexception.h>
#include <nobnc/nodebug.h>
#include <nobnc/nodir.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <iostream>

#if defined(HAVE_LIBSSL) && defined(HAVE_PTHREAD)
#include <nobnc/nomutex.h>
#include <openssl/crypto.h>
#include <memory>

static std::vector<std::unique_ptr<NoMutex>> lock_cs;

static void locking_callback(int mode, int type, const char* file, int line)
{
    if (mode & CRYPTO_LOCK) {
        lock_cs[type]->lock();
    } else {
        lock_cs[type]->unlock();
    }
}

static ulong thread_id_callback()
{
    return (ulong)pthread_self();
}

static CRYPTO_dynlock_value* dyn_create_callback(const char* file, int line)
{
    return (CRYPTO_dynlock_value*)new NoMutex;
}

static void dyn_lock_callback(int mode, CRYPTO_dynlock_value* dlock, const char* file, int line)
{
    NoMutex* mtx = (NoMutex*)dlock;

    if (mode & CRYPTO_LOCK) {
        mtx->lock();
    } else {
        mtx->unlock();
    }
}

static void dyn_destroy_callback(CRYPTO_dynlock_value* dlock, const char* file, int line)
{
    NoMutex* mtx = (NoMutex*)dlock;

    delete mtx;
}

static void thread_setup()
{
    lock_cs.resize(CRYPTO_num_locks());

    for (std::unique_ptr<NoMutex>& mtx : lock_cs)
        mtx = std::unique_ptr<NoMutex>(new NoMutex());

    CRYPTO_set_id_callback(&thread_id_callback);
    CRYPTO_set_locking_callback(&locking_callback);

    CRYPTO_set_dynlock_create_callback(&dyn_create_callback);
    CRYPTO_set_dynlock_lock_callback(&dyn_lock_callback);
    CRYPTO_set_dynlock_destroy_callback(&dyn_destroy_callback);
}

#else
#define thread_setup()
#endif

#ifdef HAVE_GETOPT_LONG
#include <getopt.h>
#else
#define no_argument 0
#define required_argument 1
#define optional_argument 2

struct option
{
    const char* a;
    int opt;
    int* flag;
    int val;
};

static inline int getopt_long(int argc, char* const argv[], const char* optstring, const struct option*, int*)
{
    return getopt(argc, argv, optstring);
}
#endif

static const struct option g_LongOpts[] = { { "help", no_argument, nullptr, 'h' },
                                            { "version", no_argument, nullptr, 'v' },
                                            { "debug", no_argument, nullptr, 'D' },
                                            { "foreground", no_argument, nullptr, 'f' },
                                            { "no-color", no_argument, nullptr, 'n' },
                                            { "allow-root", no_argument, nullptr, 'r' },
                                            { "makeconf", no_argument, nullptr, 'c' },
                                            { "makepass", no_argument, nullptr, 's' },
                                            { "makepem", no_argument, nullptr, 'p' },
                                            { "datadir", required_argument, nullptr, 'd' },
                                            { nullptr, 0, nullptr, 0 } };

static void GenerateHelp(const char* appname)
{
    No::printMessage("USAGE: " + NoString(appname) + " [options]");
    No::printMessage("Options are:");
    No::printMessage("\t-h, --help         List available command line options (this page)");
    No::printMessage("\t-v, --version      Output version information and exit");
    No::printMessage("\t-f, --foreground   Don't fork into the background");
    No::printMessage("\t-D, --debug        Output debugging information (Implies -f)");
    No::printMessage("\t-n, --no-color     Don't use escape sequences in the output");
    No::printMessage("\t-r, --allow-root   Don't complain if ZNC is run as root");
    No::printMessage("\t-c, --makeconf     Interactively create a new config");
    No::printMessage("\t-s, --makepass     Generates a password for use in config");
#ifdef HAVE_LIBSSL
    No::printMessage("\t-p, --makepem      Generates a pemfile for use with SSL");
#endif // HAVE_LIBSSL
    No::printMessage("\t-d, --datadir      Set a different ZNC repository (default is ~/.znc)");
}

static void die(int sig)
{
    signal(SIGPIPE, SIG_DFL);

    No::printMessage("Exiting on SIG [" + NoString(sig) + "]");

    delete NoAppPrivate::get(noApp);

    exit(sig);
}

static void signalHandler(int sig)
{
    switch (sig) {
    case SIGHUP:
        No::printMessage("Caught SIGHUP");
        noApp->setConfigState(NoApp::ConfigNeedRehash);
        break;
    case SIGUSR1:
        No::printMessage("Caught SIGUSR1");
        noApp->setConfigState(NoApp::ConfigNeedVerboseWrite);
        break;
    default:
        No::printMessage("WTF? Signal handler called for a signal it doesn't know?");
    }
}

static bool isRoot()
{
    // User root? If one of these were root, we could switch the others to root, too
    return (geteuid() == 0 || getuid() == 0);
}

static void seedPRNG()
{
    struct timeval tv;
    uint seed;

    // Try to find a seed which can't be as easily guessed as only time()

    if (gettimeofday(&tv, nullptr) == 0) {
        seed = (uint)tv.tv_sec;

        // This is in [0:1e6], which means that roughly 20 bits are
        // actually used, let's try to shuffle the high bits.
        seed ^= uint32_t((tv.tv_usec << 10) | tv.tv_usec);
    } else
        seed = (uint)time(nullptr);

    seed ^= rand();
    seed ^= getpid();

    srand(seed);
}

int main(int argc, char** argv)
{
    NoString config;
    NoString dataDir = "";

    thread_setup();

    seedPRNG();
    NoDebug::setFormatted(isatty(1));

    int iArg, iOptIndex = -1;
    bool bMakeConf = false;
    bool bMakePass = false;
    bool allowRoot = false;
    bool bForeground = false;
#ifdef ALWAYS_RUN_IN_FOREGROUND
    bForeground = true;
#endif
#ifdef HAVE_LIBSSL
    bool bMakePem = false;
#endif
    NoApp app;

    while ((iArg = getopt_long(argc, argv, "hvnrcspd:Df", g_LongOpts, &iOptIndex)) != -1) {
        switch (iArg) {
        case 'h':
            GenerateHelp(argv[0]);
            return 0;
        case 'v':
            std::cout << NoApp::tag() << std::endl;
            std::cout << NoApp::compileOptionsString() << std::endl;
            return 0;
        case 'n':
            NoDebug::setFormatted(false);
            break;
        case 'r':
            allowRoot = true;
            break;
        case 'c':
            bMakeConf = true;
            break;
        case 's':
            bMakePass = true;
            break;
        case 'p':
#ifdef HAVE_LIBSSL
            bMakePem = true;
            break;
#else
            No::PrintError("ZNC is compiled without SSL support.");
            return 1;
#endif // HAVE_LIBSSL
        case 'd':
            dataDir = NoString(optarg);
            break;
        case 'f':
            bForeground = true;
            break;
        case 'D':
            bForeground = true;
            NoDebug::setEnabled(true);
            break;
        case '?':
        default:
            GenerateHelp(argv[0]);
            return 1;
        }
    }

    if (optind < argc) {
        No::printError("Specifying a config file as an argument isn't supported anymore.");
        No::printError("Use --datadir instead.");
        return 1;
    }

    app.initDirs((argc ? argv[0] : ""), dataDir);

#ifdef HAVE_LIBSSL
    if (bMakePem) {
        app.writePemFile();
        return 0;
    }
#endif // HAVE_LIBSSL

    if (bMakePass) {
        NoString salt;
        No::printMessage("Type your new password.");
        NoString sHash = No::getSaltedHashPass(salt);
        No::printMessage("Kill ZNC process, if it's running.");
        No::printMessage("Then replace password in the <User> section of your config with this:");
        // Not PrintMessage(), to remove [**] from the beginning, to ease copypasting
        std::cout << "<Pass password>" << std::endl;
        std::cout << "\tMethod = " << No::defaultHash() << std::endl;
        std::cout << "\tHash = " << sHash << std::endl;
        std::cout << "\tSalt = " << salt << std::endl;
        std::cout << "</Pass>" << std::endl;
        No::printMessage("After that start ZNC again, and you should be able to login with the new password.");
        return 0;
    }

    {
        std::set<NoModuleInfo> ssGlobalMods;
        std::set<NoModuleInfo> ssUserMods;
        std::set<NoModuleInfo> ssNetworkMods;
        No::printAction("Checking for list of available modules");
        app.loader()->availableModules(ssGlobalMods, No::GlobalModule);
        app.loader()->availableModules(ssUserMods, No::UserModule);
        app.loader()->availableModules(ssNetworkMods, No::NetworkModule);
        if (ssGlobalMods.empty() && ssUserMods.empty() && ssNetworkMods.empty()) {
            No::printStatus(false, "");
            No::printError("No modules found. Perhaps you didn't install ZNC properly?");
            No::printError("Read http://wiki.znc.in/Installation for instructions.");
            if (!No::getBoolInput("Do you really want to run ZNC without any modules?", false)) {
                return 1;
            }
        }
        No::printStatus(true, "");
    }

    if (isRoot()) {
        No::printError("You are running ZNC as root! Don't do that! There are not many valid");
        No::printError("reasons for this and it can, in theory, cause great damage!");
        if (!allowRoot) {
            return 1;
        }
        No::printError("You have been warned.");
        No::printError("Hit CTRL+C now if you don't want to run ZNC as root.");
        No::printError("ZNC will start in 30 seconds.");
        sleep(30);
    }

    if (bMakeConf) {
        if (!app.writeNewConfig(config)) {
            return 0;
        }
        /* Fall through to normal bootup */
    }

    NoString configError;
    if (!app.parseConfig(config, configError)) {
        No::printError("Unrecoverable config error.");
        return 1;
    }

    if (!app.onBoot()) {
        No::printError("Exiting due to module boot errors.");
        return 1;
    }

    // TODO: NoDir(app.appPath()).filePath("znc.pid")
    PidFile pidFile("");

    if (bForeground) {
        int pid = getpid();
        No::printMessage("Staying open for debugging [pid: " + NoString(pid) + "]");

        pidFile.write(pid);
        No::printMessage(NoApp::tag());
    } else {
        No::printAction("Forking into the background");

        int pid = fork();

        if (pid == -1) {
            No::printStatus(false, strerror(errno));
            return 1;
        }

        if (pid > 0) {
            // We are the parent. We are done and will go to bed.
            No::printStatus(true, "[pid: " + NoString(pid) + "]");

            pidFile.write(pid);
            No::printMessage(NoApp::tag());
            pidFile.reset();
            return 0;
        }

        /* fcntl() locks don't necessarily propagate to forked()
         *   children.  Reacquire the lock here.  Use the blocking
         *   call to avoid race condition with parent exiting.
         */
        if (!app.waitForChildLock()) {
            No::printError("Child was unable to obtain lock on config file.");
            return 1;
        }

        // Redirect std in/out/err to /dev/null
        close(0);
        open("/dev/null", O_RDONLY);
        close(1);
        open("/dev/null", O_WRONLY);
        close(2);
        open("/dev/null", O_WRONLY);

        NoDebug::setFormatted(false);

        // We are the child. There is no way we can be a process group
        // leader, thus setsid() must succeed.
        setsid();
        // Now we are in our own process group and session (no
        // controlling terminal). We are independent!
    }

    struct sigaction sa;
    sa.sa_flags = 0;
    sigemptyset(&sa.sa_mask);

    sa.sa_handler = SIG_IGN;
    sigaction(SIGPIPE, &sa, (struct sigaction*)nullptr);

    sa.sa_handler = signalHandler;
    sigaction(SIGHUP, &sa, (struct sigaction*)nullptr);
    sigaction(SIGUSR1, &sa, (struct sigaction*)nullptr);

    // Once this signal is caught, the signal handler is reset
    // to SIG_DFL. This avoids endless loop with signals.
    sa.sa_flags = SA_RESETHAND;
    sa.sa_handler = die;
    sigaction(SIGINT, &sa, (struct sigaction*)nullptr);
    sigaction(SIGQUIT, &sa, (struct sigaction*)nullptr);
    sigaction(SIGTERM, &sa, (struct sigaction*)nullptr);

    int iRet = 0;

    try {
        iRet = app.exec();
    } catch (const NoException& e) {
        switch (e.type()) {
        case NoException::Shutdown:
            iRet = 0;
            break;
        case NoException::Restart: {
            // strdup() because GCC is stupid
            char* args[] = { strdup(argv[0]), strdup("--datadir"), strdup(app.appPath().c_str()), nullptr,
                             nullptr,         nullptr,             nullptr };
            int pos = 3;
            if (NoDebug::isEnabled())
                args[pos++] = strdup("--debug");
            else if (bForeground)
                args[pos++] = strdup("--foreground");
            if (!NoDebug::isFormatted())
                args[pos++] = strdup("--no-color");
            if (allowRoot)
                args[pos++] = strdup("--allow-root");
            // The above code adds 3 entries to args tops
            // which means the array should be big enough

            execvp(args[0], args);
            No::printError("Unable to restart ZNC [" + NoString(strerror(errno)) + "]");
        } /* Fall through */
        default:
            iRet = 1;
        }
    }

    return iRet;
}
<commit_msg>Kill that ALWAYS_RUN_IN_FOREGROUND define<commit_after>/*
 * Copyright (C) 2015 NoBNC
 * Copyright (C) 2004-2015 ZNC, see the NOTICE file for details.
 *
 * 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.
 */

#include "pidfile.h"
#include <nobnc/noapp_p.h>
#include <nobnc/nomodulecall.h>
#include <nobnc/noexception.h>
#include <nobnc/nodebug.h>
#include <nobnc/nodir.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <iostream>

#if defined(HAVE_LIBSSL) && defined(HAVE_PTHREAD)
#include <nobnc/nomutex.h>
#include <openssl/crypto.h>
#include <memory>

static std::vector<std::unique_ptr<NoMutex>> lock_cs;

static void locking_callback(int mode, int type, const char* file, int line)
{
    if (mode & CRYPTO_LOCK) {
        lock_cs[type]->lock();
    } else {
        lock_cs[type]->unlock();
    }
}

static ulong thread_id_callback()
{
    return (ulong)pthread_self();
}

static CRYPTO_dynlock_value* dyn_create_callback(const char* file, int line)
{
    return (CRYPTO_dynlock_value*)new NoMutex;
}

static void dyn_lock_callback(int mode, CRYPTO_dynlock_value* dlock, const char* file, int line)
{
    NoMutex* mtx = (NoMutex*)dlock;

    if (mode & CRYPTO_LOCK) {
        mtx->lock();
    } else {
        mtx->unlock();
    }
}

static void dyn_destroy_callback(CRYPTO_dynlock_value* dlock, const char* file, int line)
{
    NoMutex* mtx = (NoMutex*)dlock;

    delete mtx;
}

static void thread_setup()
{
    lock_cs.resize(CRYPTO_num_locks());

    for (std::unique_ptr<NoMutex>& mtx : lock_cs)
        mtx = std::unique_ptr<NoMutex>(new NoMutex());

    CRYPTO_set_id_callback(&thread_id_callback);
    CRYPTO_set_locking_callback(&locking_callback);

    CRYPTO_set_dynlock_create_callback(&dyn_create_callback);
    CRYPTO_set_dynlock_lock_callback(&dyn_lock_callback);
    CRYPTO_set_dynlock_destroy_callback(&dyn_destroy_callback);
}

#else
#define thread_setup()
#endif

#ifdef HAVE_GETOPT_LONG
#include <getopt.h>
#else
#define no_argument 0
#define required_argument 1
#define optional_argument 2

struct option
{
    const char* a;
    int opt;
    int* flag;
    int val;
};

static inline int getopt_long(int argc, char* const argv[], const char* optstring, const struct option*, int*)
{
    return getopt(argc, argv, optstring);
}
#endif

static const struct option g_LongOpts[] = { { "help", no_argument, nullptr, 'h' },
                                            { "version", no_argument, nullptr, 'v' },
                                            { "debug", no_argument, nullptr, 'D' },
                                            { "foreground", no_argument, nullptr, 'f' },
                                            { "no-color", no_argument, nullptr, 'n' },
                                            { "allow-root", no_argument, nullptr, 'r' },
                                            { "makeconf", no_argument, nullptr, 'c' },
                                            { "makepass", no_argument, nullptr, 's' },
                                            { "makepem", no_argument, nullptr, 'p' },
                                            { "datadir", required_argument, nullptr, 'd' },
                                            { nullptr, 0, nullptr, 0 } };

static void GenerateHelp(const char* appname)
{
    No::printMessage("USAGE: " + NoString(appname) + " [options]");
    No::printMessage("Options are:");
    No::printMessage("\t-h, --help         List available command line options (this page)");
    No::printMessage("\t-v, --version      Output version information and exit");
    No::printMessage("\t-f, --foreground   Don't fork into the background");
    No::printMessage("\t-D, --debug        Output debugging information (Implies -f)");
    No::printMessage("\t-n, --no-color     Don't use escape sequences in the output");
    No::printMessage("\t-r, --allow-root   Don't complain if ZNC is run as root");
    No::printMessage("\t-c, --makeconf     Interactively create a new config");
    No::printMessage("\t-s, --makepass     Generates a password for use in config");
#ifdef HAVE_LIBSSL
    No::printMessage("\t-p, --makepem      Generates a pemfile for use with SSL");
#endif // HAVE_LIBSSL
    No::printMessage("\t-d, --datadir      Set a different ZNC repository (default is ~/.znc)");
}

static void die(int sig)
{
    signal(SIGPIPE, SIG_DFL);

    No::printMessage("Exiting on SIG [" + NoString(sig) + "]");

    delete NoAppPrivate::get(noApp);

    exit(sig);
}

static void signalHandler(int sig)
{
    switch (sig) {
    case SIGHUP:
        No::printMessage("Caught SIGHUP");
        noApp->setConfigState(NoApp::ConfigNeedRehash);
        break;
    case SIGUSR1:
        No::printMessage("Caught SIGUSR1");
        noApp->setConfigState(NoApp::ConfigNeedVerboseWrite);
        break;
    default:
        No::printMessage("WTF? Signal handler called for a signal it doesn't know?");
    }
}

static bool isRoot()
{
    // User root? If one of these were root, we could switch the others to root, too
    return (geteuid() == 0 || getuid() == 0);
}

static void seedPRNG()
{
    struct timeval tv;
    uint seed;

    // Try to find a seed which can't be as easily guessed as only time()

    if (gettimeofday(&tv, nullptr) == 0) {
        seed = (uint)tv.tv_sec;

        // This is in [0:1e6], which means that roughly 20 bits are
        // actually used, let's try to shuffle the high bits.
        seed ^= uint32_t((tv.tv_usec << 10) | tv.tv_usec);
    } else
        seed = (uint)time(nullptr);

    seed ^= rand();
    seed ^= getpid();

    srand(seed);
}

int main(int argc, char** argv)
{
    NoString config;
    NoString dataDir = "";

    thread_setup();

    seedPRNG();
    NoDebug::setFormatted(isatty(1));

    int iArg, iOptIndex = -1;
    bool bMakeConf = false;
    bool bMakePass = false;
    bool allowRoot = false;
    bool bForeground = false;
#ifdef HAVE_LIBSSL
    bool bMakePem = false;
#endif
    NoApp app;

    while ((iArg = getopt_long(argc, argv, "hvnrcspd:Df", g_LongOpts, &iOptIndex)) != -1) {
        switch (iArg) {
        case 'h':
            GenerateHelp(argv[0]);
            return 0;
        case 'v':
            std::cout << NoApp::tag() << std::endl;
            std::cout << NoApp::compileOptionsString() << std::endl;
            return 0;
        case 'n':
            NoDebug::setFormatted(false);
            break;
        case 'r':
            allowRoot = true;
            break;
        case 'c':
            bMakeConf = true;
            break;
        case 's':
            bMakePass = true;
            break;
        case 'p':
#ifdef HAVE_LIBSSL
            bMakePem = true;
            break;
#else
            No::PrintError("ZNC is compiled without SSL support.");
            return 1;
#endif // HAVE_LIBSSL
        case 'd':
            dataDir = NoString(optarg);
            break;
        case 'f':
            bForeground = true;
            break;
        case 'D':
            bForeground = true;
            NoDebug::setEnabled(true);
            break;
        case '?':
        default:
            GenerateHelp(argv[0]);
            return 1;
        }
    }

    if (optind < argc) {
        No::printError("Specifying a config file as an argument isn't supported anymore.");
        No::printError("Use --datadir instead.");
        return 1;
    }

    app.initDirs((argc ? argv[0] : ""), dataDir);

#ifdef HAVE_LIBSSL
    if (bMakePem) {
        app.writePemFile();
        return 0;
    }
#endif // HAVE_LIBSSL

    if (bMakePass) {
        NoString salt;
        No::printMessage("Type your new password.");
        NoString sHash = No::getSaltedHashPass(salt);
        No::printMessage("Kill ZNC process, if it's running.");
        No::printMessage("Then replace password in the <User> section of your config with this:");
        // Not PrintMessage(), to remove [**] from the beginning, to ease copypasting
        std::cout << "<Pass password>" << std::endl;
        std::cout << "\tMethod = " << No::defaultHash() << std::endl;
        std::cout << "\tHash = " << sHash << std::endl;
        std::cout << "\tSalt = " << salt << std::endl;
        std::cout << "</Pass>" << std::endl;
        No::printMessage("After that start ZNC again, and you should be able to login with the new password.");
        return 0;
    }

    {
        std::set<NoModuleInfo> ssGlobalMods;
        std::set<NoModuleInfo> ssUserMods;
        std::set<NoModuleInfo> ssNetworkMods;
        No::printAction("Checking for list of available modules");
        app.loader()->availableModules(ssGlobalMods, No::GlobalModule);
        app.loader()->availableModules(ssUserMods, No::UserModule);
        app.loader()->availableModules(ssNetworkMods, No::NetworkModule);
        if (ssGlobalMods.empty() && ssUserMods.empty() && ssNetworkMods.empty()) {
            No::printStatus(false, "");
            No::printError("No modules found. Perhaps you didn't install ZNC properly?");
            No::printError("Read http://wiki.znc.in/Installation for instructions.");
            if (!No::getBoolInput("Do you really want to run ZNC without any modules?", false)) {
                return 1;
            }
        }
        No::printStatus(true, "");
    }

    if (isRoot()) {
        No::printError("You are running ZNC as root! Don't do that! There are not many valid");
        No::printError("reasons for this and it can, in theory, cause great damage!");
        if (!allowRoot) {
            return 1;
        }
        No::printError("You have been warned.");
        No::printError("Hit CTRL+C now if you don't want to run ZNC as root.");
        No::printError("ZNC will start in 30 seconds.");
        sleep(30);
    }

    if (bMakeConf) {
        if (!app.writeNewConfig(config)) {
            return 0;
        }
        /* Fall through to normal bootup */
    }

    NoString configError;
    if (!app.parseConfig(config, configError)) {
        No::printError("Unrecoverable config error.");
        return 1;
    }

    if (!app.onBoot()) {
        No::printError("Exiting due to module boot errors.");
        return 1;
    }

    // TODO: NoDir(app.appPath()).filePath("znc.pid")
    PidFile pidFile("");

    if (bForeground) {
        int pid = getpid();
        No::printMessage("Staying open for debugging [pid: " + NoString(pid) + "]");

        pidFile.write(pid);
        No::printMessage(NoApp::tag());
    } else {
        No::printAction("Forking into the background");

        int pid = fork();

        if (pid == -1) {
            No::printStatus(false, strerror(errno));
            return 1;
        }

        if (pid > 0) {
            // We are the parent. We are done and will go to bed.
            No::printStatus(true, "[pid: " + NoString(pid) + "]");

            pidFile.write(pid);
            No::printMessage(NoApp::tag());
            pidFile.reset();
            return 0;
        }

        /* fcntl() locks don't necessarily propagate to forked()
         *   children.  Reacquire the lock here.  Use the blocking
         *   call to avoid race condition with parent exiting.
         */
        if (!app.waitForChildLock()) {
            No::printError("Child was unable to obtain lock on config file.");
            return 1;
        }

        // Redirect std in/out/err to /dev/null
        close(0);
        open("/dev/null", O_RDONLY);
        close(1);
        open("/dev/null", O_WRONLY);
        close(2);
        open("/dev/null", O_WRONLY);

        NoDebug::setFormatted(false);

        // We are the child. There is no way we can be a process group
        // leader, thus setsid() must succeed.
        setsid();
        // Now we are in our own process group and session (no
        // controlling terminal). We are independent!
    }

    struct sigaction sa;
    sa.sa_flags = 0;
    sigemptyset(&sa.sa_mask);

    sa.sa_handler = SIG_IGN;
    sigaction(SIGPIPE, &sa, (struct sigaction*)nullptr);

    sa.sa_handler = signalHandler;
    sigaction(SIGHUP, &sa, (struct sigaction*)nullptr);
    sigaction(SIGUSR1, &sa, (struct sigaction*)nullptr);

    // Once this signal is caught, the signal handler is reset
    // to SIG_DFL. This avoids endless loop with signals.
    sa.sa_flags = SA_RESETHAND;
    sa.sa_handler = die;
    sigaction(SIGINT, &sa, (struct sigaction*)nullptr);
    sigaction(SIGQUIT, &sa, (struct sigaction*)nullptr);
    sigaction(SIGTERM, &sa, (struct sigaction*)nullptr);

    int iRet = 0;

    try {
        iRet = app.exec();
    } catch (const NoException& e) {
        switch (e.type()) {
        case NoException::Shutdown:
            iRet = 0;
            break;
        case NoException::Restart: {
            // strdup() because GCC is stupid
            char* args[] = { strdup(argv[0]), strdup("--datadir"), strdup(app.appPath().c_str()), nullptr,
                             nullptr,         nullptr,             nullptr };
            int pos = 3;
            if (NoDebug::isEnabled())
                args[pos++] = strdup("--debug");
            else if (bForeground)
                args[pos++] = strdup("--foreground");
            if (!NoDebug::isFormatted())
                args[pos++] = strdup("--no-color");
            if (allowRoot)
                args[pos++] = strdup("--allow-root");
            // The above code adds 3 entries to args tops
            // which means the array should be big enough

            execvp(args[0], args);
            No::printError("Unable to restart ZNC [" + NoString(strerror(errno)) + "]");
        } /* Fall through */
        default:
            iRet = 1;
        }
    }

    return iRet;
}
<|endoftext|>


<commit_before>/*
  Copyright (c) 2014 Noel R. Cower

  Permission is hereby granted, free of charge, to any person obtaining
  a copy of this software and associated documentation files (the "Software"),
  to deal in the Software without restriction, including without limitation
  the rights to use, copy, modify, merge, publish, distribute, sublicense,
  and/or sell copies of the Software, and to permit persons to whom
  the Software is furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included
  in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USEOR OTHER DEALINGS
  IN THE SOFTWARE.
*/

#include "bufstream.hh"

#include <sstream>

typedef std::basic_stringstream<
#if __cplusplus >= 201103L
  char,
  std::char_traits<char>,
  sz_cxx_allocator_t<char>
#else
  char,
  std::char_traits<char>
#endif
  > sz_buffer_t;

static
size_t
sz_bufstream_read(void *out, size_t length, sz_stream_t *stream);

static
size_t
sz_bufstream_write(const void *in, size_t length, sz_stream_t *stream);

static
off_t
sz_bufstream_seek(off_t off, int whence, sz_stream_t *stream);

static
int
sz_bufstream_eof(sz_stream_t *stream);

static
void
sz_bufstream_close(sz_stream_t *stream);

struct SZ_HIDDEN sz_bufstream_t
{
  sz_stream_t base;
  sz_allocator_t *alloc;
  sz_mode_t mode;
  uint8_t opaque[sizeof(sz_buffer_t)];

  sz_buffer_t *buffer()
  {
    return (sz_buffer_t *)&opaque[0];
  }

  void init()
  {
    new (opaque) sz_buffer_t(
    #if __cplusplus >= 201103L
      sz_bufstring_t(sz_cxx_allocator_t<char>(alloc))
    #endif
      );
  }

  void finalize()
  {
    buffer()->~sz_buffer_t();
  }
};

static sz_stream_t sz_bufstream_base = {
  sz_bufstream_read,
  sz_bufstream_write,
  sz_bufstream_seek,
  sz_bufstream_eof,
  sz_bufstream_close
};

sz_stream_t *
sz_buffer_stream(sz_mode_t mode, sz_allocator_t *alloc)
{
  sz_bufstream_t *stream = (sz_bufstream_t *)sz_malloc(sizeof(sz_bufstream_t), alloc);
  stream->base = sz_bufstream_base;
  stream->init();
  stream->mode = mode;
  return (sz_stream_t *)stream;
}

sz_bufstring_t
sz_buffer_stream_data(sz_stream_t *stream)
{
  return ((sz_bufstream_t *)stream)->buffer()->str();
}

static
size_t
sz_bufstream_read(void *out, size_t length, sz_stream_t *stream)
{
  sz_bufstream_t *bufstream = (sz_bufstream_t *)stream;
  if (bufstream->mode == SZ_READER) {
    bufstream->buffer()->read((char *)out, length);
    return bufstream->buffer()->gcount();
  }
  return 0;
}

static
size_t
sz_bufstream_write(const void *in, size_t length, sz_stream_t *stream)
{
  sz_bufstream_t *bufstream = (sz_bufstream_t *)stream;
  if (bufstream->mode == SZ_READER) {
    off_t offset = bufstream->buffer()->tellp();
    bufstream->buffer()->write((const char *)in, length);
    return size_t(bufstream->buffer()->tellp() - offset);
  }
  return 0;
}

static
off_t
sz_bufstream_seek(off_t off, int whence, sz_stream_t *stream)
{
  sz_bufstream_t *bufstream = (sz_bufstream_t *)stream;
  sz_buffer_t::seekdir dir = sz_buffer_t::seekdir::beg;
  switch (whence) {
  case SEEK_CUR: dir = sz_buffer_t::seekdir::cur; break;
  case SEEK_END: dir = sz_buffer_t::seekdir::end; break;
  case SEEK_SET:
  default: break;
  }

  if (bufstream->mode == SZ_WRITER) {
    bufstream->buffer()->seekp(off, dir);
    return bufstream->buffer()->tellp();
  } else {
    bufstream->buffer()->seekg(off, dir);
    return bufstream->buffer()->tellg();
  }
}

static
int
sz_bufstream_eof(sz_stream_t *stream)
{
  return ((sz_bufstream_t *)stream)->buffer()->eof();
}

static
void
sz_bufstream_close(sz_stream_t *stream)
{
  sz_bufstream_t *bufstream = (sz_bufstream_t *)stream;
  bufstream->finalize();
  sz_free(bufstream, bufstream->alloc);
}
<commit_msg>Drop seekdir:: resolution.<commit_after>/*
  Copyright (c) 2014 Noel R. Cower

  Permission is hereby granted, free of charge, to any person obtaining
  a copy of this software and associated documentation files (the "Software"),
  to deal in the Software without restriction, including without limitation
  the rights to use, copy, modify, merge, publish, distribute, sublicense,
  and/or sell copies of the Software, and to permit persons to whom
  the Software is furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included
  in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USEOR OTHER DEALINGS
  IN THE SOFTWARE.
*/

#include "bufstream.hh"

#include <sstream>

typedef std::basic_stringstream<
#if __cplusplus >= 201103L
  char,
  std::char_traits<char>,
  sz_cxx_allocator_t<char>
#else
  char,
  std::char_traits<char>
#endif
  > sz_buffer_t;

static
size_t
sz_bufstream_read(void *out, size_t length, sz_stream_t *stream);

static
size_t
sz_bufstream_write(const void *in, size_t length, sz_stream_t *stream);

static
off_t
sz_bufstream_seek(off_t off, int whence, sz_stream_t *stream);

static
int
sz_bufstream_eof(sz_stream_t *stream);

static
void
sz_bufstream_close(sz_stream_t *stream);

struct SZ_HIDDEN sz_bufstream_t
{
  sz_stream_t base;
  sz_allocator_t *alloc;
  sz_mode_t mode;
  uint8_t opaque[sizeof(sz_buffer_t)];

  sz_buffer_t *buffer()
  {
    return (sz_buffer_t *)&opaque[0];
  }

  void init()
  {
    new (opaque) sz_buffer_t(
    #if __cplusplus >= 201103L
      sz_bufstring_t(sz_cxx_allocator_t<char>(alloc))
    #endif
      );
  }

  void finalize()
  {
    buffer()->~sz_buffer_t();
  }
};

static sz_stream_t sz_bufstream_base = {
  sz_bufstream_read,
  sz_bufstream_write,
  sz_bufstream_seek,
  sz_bufstream_eof,
  sz_bufstream_close
};

sz_stream_t *
sz_buffer_stream(sz_mode_t mode, sz_allocator_t *alloc)
{
  sz_bufstream_t *stream = (sz_bufstream_t *)sz_malloc(sizeof(sz_bufstream_t), alloc);
  stream->base = sz_bufstream_base;
  stream->init();
  stream->mode = mode;
  return (sz_stream_t *)stream;
}

sz_bufstring_t
sz_buffer_stream_data(sz_stream_t *stream)
{
  return ((sz_bufstream_t *)stream)->buffer()->str();
}

static
size_t
sz_bufstream_read(void *out, size_t length, sz_stream_t *stream)
{
  sz_bufstream_t *bufstream = (sz_bufstream_t *)stream;
  if (bufstream->mode == SZ_READER) {
    bufstream->buffer()->read((char *)out, length);
    return bufstream->buffer()->gcount();
  }
  return 0;
}

static
size_t
sz_bufstream_write(const void *in, size_t length, sz_stream_t *stream)
{
  sz_bufstream_t *bufstream = (sz_bufstream_t *)stream;
  if (bufstream->mode == SZ_READER) {
    off_t offset = bufstream->buffer()->tellp();
    bufstream->buffer()->write((const char *)in, length);
    return size_t(bufstream->buffer()->tellp() - offset);
  }
  return 0;
}

static
off_t
sz_bufstream_seek(off_t off, int whence, sz_stream_t *stream)
{
  sz_bufstream_t *bufstream = (sz_bufstream_t *)stream;
  sz_buffer_t::seekdir dir = sz_buffer_t::beg;
  switch (whence) {
  case SEEK_CUR: dir = sz_buffer_t::cur; break;
  case SEEK_END: dir = sz_buffer_t::end; break;
  case SEEK_SET:
  default: break;
  }

  if (bufstream->mode == SZ_WRITER) {
    bufstream->buffer()->seekp(off, dir);
    return bufstream->buffer()->tellp();
  } else {
    bufstream->buffer()->seekg(off, dir);
    return bufstream->buffer()->tellg();
  }
}

static
int
sz_bufstream_eof(sz_stream_t *stream)
{
  return ((sz_bufstream_t *)stream)->buffer()->eof();
}

static
void
sz_bufstream_close(sz_stream_t *stream)
{
  sz_bufstream_t *bufstream = (sz_bufstream_t *)stream;
  bufstream->finalize();
  sz_free(bufstream, bufstream->alloc);
}
<|endoftext|>


<commit_before>
#ifndef __MESSAGEBASE_HPP__
#define __MESSAGEBASE_HPP__

#include <cstring>

#include "ConditionVariable.hpp"

typedef int16_t Core;

namespace Grappa {

  /// Internal messaging functions
  namespace impl {

    /// @addtogroup Communication
    /// @{

    /// Base class for messages. Includes support for the three main features of a message:
    ///  - pointers and flags to support storing messages in a linked list
    ///  - interface for message serialization
    ///  - storage for blocking worker until message is sent
    /// This is a virtual class.
    class MessageBase {
    private:
      MessageBase * next_;     ///< what's the next message in the list of messages to be sent?
      MessageBase * prefetch_; ///< what's the next message to prefetch?

      Core destination_;       ///< What core is this message aimed at?

      bool is_enqueued_;       ///< Have we been added to the send queue?
      bool is_sent_;           ///< Is our payload no longer needed?
      ConditionVariable cv_;   ///< condition variable for sleep/wake

      friend class RDMAAggregator;

      /// Active message handler to support sending messages through old aggregator
      static void legacy_send_message_am( char * buf, size_t size, void * payload, size_t payload_size ) {
        Grappa::impl::MessageBase::deserialize_buffer( static_cast< char * >( buf ), size );
      }

      /// Send message through old aggregator
      void legacy_send() {
        const size_t message_size = this->size();
        char buf[ message_size ];
        this->serialize_to( buf, message_size );
        Grappa_call_on( destination_, legacy_send_message_am, buf, message_size );
        is_sent_ = true;
      }

      /// Interface for message serialization.
      ///  @param p Address in buffer at which to write:
      ///    -# A 2D global address of a function that knows how to
      ///       deserialize and execute the message functor/payload
      ///    -# the message functor/payload
      /// @return address of the byte following the serialized message in the buffer
      virtual char * serialize_to( char * p, size_t max_size = -1 ) = 0;

      /// Add a message to this message's list
      inline void stitch( MessageBase * m ) {
        m->next_ = next_;
        next_ = m;
      }

      /// Chase a list of messages and serialize them into a buffer.
      /// Modifies pointer to list to support size-limited-ish aggregation
      /// TODO: make this a hard limit?
      static char * serialize_to_buffer( char * buffer, Grappa::impl::MessageBase ** message_ptr, size_t max = -1 ) {
        size_t size = 0;
        Grappa::impl::MessageBase * message = *message_ptr;
        //DVLOG(5) << "Serializing messages from " << message;
        while( message ) {
          //DVLOG(5) << "Serializing message " << message;
          // add message to buffer
          char * new_buffer = message->serialize_to( buffer, max - size );
          if( new_buffer == buffer ) { // if it was too big
            break;                     // quit
          } else {
            // mark as sent
            message->is_sent_ = true;
            // go to next messsage and track total size
            Grappa::impl::MessageBase * next = message->next_;
            message->next_ = NULL;
            size += next - message;
            message = next;
          }
          buffer = new_buffer;
        }
        *message_ptr = message;
        return buffer;
      }

      /// Walk a buffer of received deserializers/functors and call them.
      static char * deserialize_buffer( char * buffer, size_t size ) {
        char * end = buffer + size;
        while( buffer < end ) {
          typedef char * (*Deserializer)(char *);       // generic deserializer type
          Deserializer fp = *(reinterpret_cast< Deserializer* >( buffer ));
          buffer = fp( buffer + sizeof( Deserializer ) );
        }
        return buffer; /// how far did we get before we had to stop?
      }

    public:
      MessageBase( )
        : next_( NULL )
        , is_enqueued_( false )
        , is_sent_( false )
        , destination_( -1 )
        , cv_()
      { }

      MessageBase( Core dest )
        : next_( NULL )
        , is_enqueued_( false )
        , is_sent_( false )
        , destination_( dest )
        , cv_()
      { }

      // Ensure we are sent before leaving scope
      inline virtual ~MessageBase() {
        block_until_sent();
      }

      /// Make sure we know how big this message is
      virtual const size_t size() const = 0;

      inline void send() {
        //Grappa::impl::global_rdma_aggregator.send( this );
      }

      /// Block until message can be deallocated.
      inline void block_until_sent() {
        // if message has not been enqueued to be sent, do so.
        if( !is_enqueued_ ) {
          send();
        }
        // now block until message is sent
        while( !is_sent_ ) {
          //global_scheduler.thread_suspend();
          Grappa::wait( &cv_ );
        }
      }

    };

    /// @}
  }
}

#endif
<commit_msg>Support legacy aggregator in scoped messages.<commit_after>
#ifndef __MESSAGEBASE_HPP__
#define __MESSAGEBASE_HPP__

#include <cstring>

#include "ConditionVariable.hpp"

typedef int16_t Core;

namespace Grappa {

  /// Internal messaging functions
  namespace impl {

    /// @addtogroup Communication
    /// @{

    /// Base class for messages. Includes support for the three main features of a message:
    ///  - pointers and flags to support storing messages in a linked list
    ///  - interface for message serialization
    ///  - storage for blocking worker until message is sent
    /// This is a virtual class.
    class MessageBase {
    private:
      MessageBase * next_;     ///< what's the next message in the list of messages to be sent?
      MessageBase * prefetch_; ///< what's the next message to prefetch?

      Core destination_;       ///< What core is this message aimed at?

      bool is_enqueued_;       ///< Have we been added to the send queue?
      bool is_sent_;           ///< Is our payload no longer needed?
      ConditionVariable cv_;   ///< condition variable for sleep/wake

      friend class RDMAAggregator;

      /// Active message handler to support sending messages through old aggregator
      static void legacy_send_message_am( char * buf, size_t size, void * payload, size_t payload_size ) {
        Grappa::impl::MessageBase::deserialize_buffer( static_cast< char * >( buf ), size );
      }

      /// Send message through old aggregator
      void legacy_send() {
        const size_t message_size = this->size();
        char buf[ message_size ];
        this->serialize_to( buf, message_size );
        Grappa_call_on( destination_, legacy_send_message_am, buf, message_size );
        is_sent_ = true;
      }

      /// Interface for message serialization.
      ///  @param p Address in buffer at which to write:
      ///    -# A 2D global address of a function that knows how to
      ///       deserialize and execute the message functor/payload
      ///    -# the message functor/payload
      /// @return address of the byte following the serialized message in the buffer
      virtual char * serialize_to( char * p, size_t max_size = -1 ) = 0;

      /// Add a message to this message's list
      inline void stitch( MessageBase * m ) {
        m->next_ = next_;
        next_ = m;
      }

      /// Chase a list of messages and serialize them into a buffer.
      /// Modifies pointer to list to support size-limited-ish aggregation
      /// TODO: make this a hard limit?
      static char * serialize_to_buffer( char * buffer, Grappa::impl::MessageBase ** message_ptr, size_t max = -1 ) {
        size_t size = 0;
        Grappa::impl::MessageBase * message = *message_ptr;
        //DVLOG(5) << "Serializing messages from " << message;
        while( message ) {
          //DVLOG(5) << "Serializing message " << message;
          // add message to buffer
          char * new_buffer = message->serialize_to( buffer, max - size );
          if( new_buffer == buffer ) { // if it was too big
            break;                     // quit
          } else {
            // mark as sent
            message->is_sent_ = true;
            // go to next messsage and track total size
            Grappa::impl::MessageBase * next = message->next_;
            message->next_ = NULL;
            size += next - message;
            message = next;
          }
          buffer = new_buffer;
        }
        *message_ptr = message;
        return buffer;
      }

      /// Walk a buffer of received deserializers/functors and call them.
      static char * deserialize_buffer( char * buffer, size_t size ) {
        char * end = buffer + size;
        while( buffer < end ) {
          typedef char * (*Deserializer)(char *);       // generic deserializer type
          Deserializer fp = *(reinterpret_cast< Deserializer* >( buffer ));
          buffer = fp( buffer + sizeof( Deserializer ) );
        }
        return buffer; /// how far did we get before we had to stop?
      }

    public:
      MessageBase( )
        : next_( NULL )
        , is_enqueued_( false )
        , is_sent_( false )
        , destination_( -1 )
        , cv_()
      { }

      MessageBase( Core dest )
        : next_( NULL )
        , is_enqueued_( false )
        , is_sent_( false )
        , destination_( dest )
        , cv_()
      { }

      // Ensure we are sent before leaving scope
      inline virtual ~MessageBase() {
        block_until_sent();
      }

      /// Make sure we know how big this message is
      virtual const size_t size() const = 0;

      inline void send() {
        //Grappa::impl::global_rdma_aggregator.send( this );
        is_enqueued_ = true;
        legacy_send();
      }

      /// Block until message can be deallocated.
      inline void block_until_sent() {
        // if message has not been enqueued to be sent, do so.
        if( !is_enqueued_ ) {
          send();
        }
        // now block until message is sent
        while( !is_sent_ ) {
          //global_scheduler.thread_suspend();
          Grappa::wait( &cv_ );
        }
      }

    };

    /// @}
  }
}

#endif
<|endoftext|>


<commit_before>#include "treecore/AtomicFunc.h"
#include "TestFramework.h"

using namespace treecore;

void TestFramework::content()
{
    {
        int var = 111;
        IS(atomic_load(&var), 111);

        atomic_store(&var, 123);
        IS(var, 123);

        IS(atomic_exchange(&var, 456), 123);
        IS(var, 456);

        // arithmetic and logic ops
        // fetch-op
        IS(atomic_fetch_add(&var, 100), 456);
        IS(var, 556);
        IS(atomic_fetch_sub(&var, 200), 556);
        IS(var, 356);
        IS(atomic_fetch_or(&var, 0xf0), 356);
        IS(var, 500);
        IS(atomic_fetch_and(&var, 56), 500);
        IS(var, 48);
        IS(atomic_fetch_xor(&var, 0xfc), 48);
        IS(var, 204);

        // op-fetch
        IS(atomic_add_fetch(&var, 123), 327);
        IS(atomic_sub_fetch(&var, 321), 6);
        IS(atomic_or_fetch(&var, 20), 22);
        IS(atomic_and_fetch(&var, 28), 20);
        IS(atomic_xor_fetch(&var, 0xff), 235);

        // CAS
        OK(atomic_compare_set(&var, 235, 100));
        IS(var, 100);
        OK(!atomic_compare_set(&var, 123, 456));
        IS(var, 100);

        int target = 100;
        OK(atomic_compare_exchange(&var, &target, 666));
        IS(var, 666);
        IS(target, 100);

        target = 123;
        OK(!atomic_compare_exchange(&var, &target, 777));
        IS(var, 666);
        IS(target, 666);
    }
}
<commit_msg>tested atomic op on non-primitive types in single thread<commit_after>#include "treecore/AtomicFunc.h"
#include "treecore/IntTypes.h"

#include "TestFramework.h"

using namespace treecore;

struct Foo
{
    int16  a;
    uint16 b;
    float  c;
};

void TestFramework::content()
{
    {
        int var = 111;
        IS(atomic_load(&var), 111);

        atomic_store(&var, 123);
        IS(var, 123);

        IS(atomic_exchange(&var, 456), 123);
        IS(var, 456);

        // arithmetic and logic ops
        // fetch-op
        IS(atomic_fetch_add(&var, 100), 456);
        IS(var, 556);
        IS(atomic_fetch_sub(&var, 200), 556);
        IS(var, 356);
        IS(atomic_fetch_or(&var, 0xf0), 356);
        IS(var, 500);
        IS(atomic_fetch_and(&var, 56), 500);
        IS(var, 48);
        IS(atomic_fetch_xor(&var, 0xfc), 48);
        IS(var, 204);

        // op-fetch
        IS(atomic_add_fetch(&var, 123), 327);
        IS(atomic_sub_fetch(&var, 321), 6);
        IS(atomic_or_fetch(&var, 20), 22);
        IS(atomic_and_fetch(&var, 28), 20);
        IS(atomic_xor_fetch(&var, 0xff), 235);

        // CAS
        OK(atomic_compare_set(&var, 235, 100));
        IS(var, 100);
        OK(!atomic_compare_set(&var, 123, 456));
        IS(var, 100);

        int target = 100;
        OK(atomic_compare_exchange(&var, &target, 666));
        IS(var, 666);
        IS(target, 100);

        target = 123;
        OK(!atomic_compare_exchange(&var, &target, 777));
        IS(var, 666);
        IS(target, 666);
    }

    {
        Foo var1{1, 5, 12.34f};
        {
            Foo tmp = atomic_load(&var1);
            IS(tmp.a, 1);
            IS(tmp.b, 5);
            IS(tmp.c, 12.34f);
        }

        atomic_store(&var1, {5, 213, 34.89f});
        IS(var1.a, 5);
        IS(var1.b, 213);
        IS(var1.c, 34.89f);

        {
            Foo tmp = atomic_exchange(&var1, {890, 666, 666.666f});
            IS(tmp.a, 5);
            IS(tmp.b, 213);
            IS(tmp.c, 34.89f);
            IS(var1.a, 890);
            IS(var1.b, 666);
            IS(var1.c, 666.666f);
        }

        // CAS
        OK(atomic_compare_set(&var1, {890, 666, 666.666f}, {1, 2, 34.5f}));
        IS(var1.a, 1);
        IS(var1.b, 2);
        IS(var1.c, 34.5f);
        OK(!atomic_compare_set(&var1, {1, 1, 1.0f}, {1, 2, 3.0f}));
        IS(var1.a, 1);
        IS(var1.b, 2);
        IS(var1.c, 34.5f);

        {
            Foo expect{1, 2, 34.5f};
            OK(atomic_compare_exchange(&var1, &expect, {2, 3, 45.6f}));
            IS(expect.a, 1);
            IS(expect.b, 2);
            IS(expect.c, 34.5f);
            IS(var1.a, 2);
            IS(var1.b, 3);
            IS(var1.c, 45.6f);

            expect = {2, 2, 22.2f};
            OK(!atomic_compare_exchange(&var1, &expect, {1, 1, 11.11f}));
            IS(expect.a, 2);
            IS(expect.b, 3);
            IS(expect.c, 45.6f);
            IS(var1.a, 2);
            IS(var1.b, 3);
            IS(var1.c, 45.6f);
        }
    }
}
<|endoftext|>