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

#include "config.h"

#include <iomanip>
#include <cstdio>
#include <ctime>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif

#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif

#include <boost/filesystem.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include "quvicpp/quvicpp.h"

#include "cclive/options.h"
#include "cclive/file.h"
#include "cclive/log.h"
#include "cclive/progressbar.h"

#if defined(SIGWINCH) && defined(TIOCGWINSZ)
#define WITH_RESIZE
#endif

namespace cclive {

#ifdef WITH_RESIZE

static volatile sig_atomic_t recv_sigwinch;

static void
handle_sigwinch (int s) {
    recv_sigwinch = 1;
    signal(SIGWINCH, handle_sigwinch);
}

static size_t
get_term_width () {

    const int fd = fileno (stderr);

    winsize wsz;

    if (ioctl (fd, TIOCGWINSZ, &wsz) < 0)
        return 0;

    return wsz.ws_col;
}
#endif // WITH_RESIZE

namespace po = boost::program_options;

progressbar::progressbar (
    const file& f,
    const quvicpp::link& l,
    const options& opts)
    : _update_interval (.2),
      _expected_bytes (l.length ()),
      _initial_bytes  (f.initial_length ()),
      _time_started (0),
      _last_update (0),
      _term_width (0),
      _dot_count  (0),
      _old_width (0),
      _count (0),
      _width (0),
      _file (f),
      _done (false),
      _mode (normal)
{
    if (_initial_bytes > _expected_bytes)
        _expected_bytes = _initial_bytes;

#ifdef WITH_RESIZE
    signal (SIGWINCH, handle_sigwinch);

    if (!_term_width || recv_sigwinch) {

        _term_width = get_term_width ();

        if (!_term_width)
            _term_width = default_term_width;
    }
#else
    _term_width = default_term_width;
#endif

    _width     = _term_width-1; // Don't use the last column.
    _old_width = _width;

    time (&_time_started);

    const po::variables_map map  = opts.map ();

    if (map.count ("background")) {
        cclive::log << _file.to_s (l) << std::endl;
        _mode = dotline;
    }

    _update_interval = map["update-interval"].as<double>();
}

static double
to_mb (const double bytes) { return bytes/(1024*1024); }

namespace pt = boost::posix_time;

static std::string
to_s (const int secs) {
    pt::time_duration td = pt::seconds (secs);
    return pt::to_simple_string (td);
}

static std::string
to_unit (double& rate) {
    int i = 0;
    if (rate < 1024*1024) {
        rate /= 1024;
    }
    else if (rate < 1024*1024) {
        rate /= 1024*1024;
        i = 1;
    }
    else if (rate < 1024*1024*1024) {
        rate /= 1024*1024*1024;
        i = 2;
    }
    static const char *units[] = {"K/s", "M/s", "G/s"};
    return units[i];
}

namespace fs = boost::filesystem;

void
progressbar::update (double now) {

    time_t tnow;

    time (&tnow);

    const time_t elapsed = tnow - _time_started;

    bool force_update = false;

#ifdef WITH_RESIZE
    if (recv_sigwinch && _mode == normal) {

        const size_t old_term_width = _term_width;

        _term_width = get_term_width ();

        if (!_term_width)
            _term_width = default_term_width;

        if (_term_width != old_term_width) {
            _old_width  = _width;
            _width = _term_width - 1; // Do not use the last column.
            force_update = true;
        }

        recv_sigwinch = 0;
    }
#endif // WITH_RESIZE

    if (!_done) {
        if ((elapsed - _last_update) < _update_interval
            && !force_update)
        {
            return;
        }
    }
    else
        now = _expected_bytes;

    // Current size.

    const double size =
        (!_done)
        ? _initial_bytes + now
        : now;

    std::stringstream size_s;

    size_s.setf (std::ios::fixed);

    size_s
        << std::setprecision (1)
        << to_mb (size)
        << "M";

    // Rate.

    double rate = elapsed ? (now/elapsed):0;

    std::stringstream rate_s, eta_s;

    rate_s.setf (std::ios::fixed);
    eta_s.setf (std::ios::fixed);

    if (rate > 0) {

        // ETA.

        std::string eta;

        if (!_done) {

            const double left =
                (_expected_bytes - (now + _initial_bytes)) / rate;

            eta = to_s (static_cast<int>(left+0.5));
        }
        else {
            rate = (_expected_bytes - _initial_bytes) / elapsed;
            eta  = to_s (elapsed);
        }

        std::string unit = to_unit (rate);

        rate_s
            << std::setw (4)
            << std::setprecision (1)
            << rate
            << unit;

        eta_s
            << std::setw (6)
            << eta;
    }
    else { // ETA: inactive (default).
        rate_s << "--.-K/s";
        eta_s << "--:--:--";
    }

    // Percent.

    std::stringstream percent_s;

    if (_expected_bytes > 0) {

        const int percent =
            static_cast<int>(100.0*size/_expected_bytes);

        if (percent < 100)
            percent_s << std::setw(2) << percent << "%";
        else
            percent_s << "100%";
    }

    // Filename.

    fs::path p = fs::system_complete (_file.path ());

    std::string fname = p.filename ();

    switch (_mode) {
    default:
    case  normal:  _normal  (size_s, rate_s, eta_s, percent_s, fname); break;
    case dotline:  _dotline (size_s, rate_s, eta_s, percent_s, fname); break;
    }

    _last_update = elapsed;
    _count       = now;
}

void
progressbar::_normal (
    const std::stringstream& size_s,
    const std::stringstream& rate_s,
    const std::stringstream& eta_s,
    const std::stringstream& percent_s,
    const std::string& fname)
{
    std::stringstream tmp;

    tmp.setf (std::ios::fixed);

    // Size.

    tmp << "  "
        << std::setw (4)
        << size_s.str ();

    // Rate, ETA.

    tmp << "  "
        << rate_s.str ()
        << "  "
        << eta_s.str ();

    // Percent.

    tmp << "  "
        << percent_s.str ();

    // Filename. Slice and dice.

    const size_t tmp_len = tmp.str ().length ();
    const int sub_len    = _width - tmp_len;

    std::stringstream b;

    if (sub_len > 0) {

        b << fname.substr (0, sub_len);

        // Pad to max. terminal width (filename <-> other details).

        while (b.str ().length () < (_width - tmp_len))
            b << " ";

        b << tmp.str ();
    }

    // Clear the last column if the terminal shrunk.

    if (_old_width > _width) {
        _old_width = _width;
        b << " ";
    }

    // Print.

    cclive::log << b.str () << "\r" << std::flush;
}

void
progressbar::_dotline (
    const std::stringstream& size_s,
    const std::stringstream& rate_s,
    const std::stringstream& eta_s,
    const std::stringstream& percent_s,
    const std::string& fname)
{
    if (++_dot_count >= 31) {
        cclive::log
            << "  "
            << std::setw (6)
            << size_s.str ()
            << "  "
            << rate_s.str ()
            << "  "
            << eta_s.str ()
            << "  "
            << percent_s.str ()
            << std::endl;
        _dot_count = 0;
    }
    else {
        cclive::log << "." << (_dot_count % 3 == 0 ? " ":"") << std::flush;
    }
}

void
progressbar::finish () {

    if (_expected_bytes > 0
        && _count + _initial_bytes > _expected_bytes)
    {
        _expected_bytes = _initial_bytes + _count;
    }

    _done = true;
    update (-1);
}

} // End namespace.

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

#include "config.h"

#include <iomanip>
#include <cstdio>
#include <ctime>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif

#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif

#include <boost/filesystem.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include "quvicpp/quvicpp.h"

#include "cclive/options.h"
#include "cclive/file.h"
#include "cclive/log.h"
#include "cclive/progressbar.h"

#if defined(SIGWINCH) && defined(TIOCGWINSZ)
#define WITH_RESIZE
#endif

namespace cclive {

#ifdef WITH_RESIZE

static volatile sig_atomic_t recv_sigwinch;

static void
handle_sigwinch (int s) {
    recv_sigwinch = 1;
    signal(SIGWINCH, handle_sigwinch);
}

static size_t
get_term_width () {

    const int fd = fileno (stderr);

    winsize wsz;

    if (ioctl (fd, TIOCGWINSZ, &wsz) < 0)
        return 0;

    return wsz.ws_col;
}
#endif // WITH_RESIZE

namespace po = boost::program_options;

progressbar::progressbar (
    const file& f,
    const quvicpp::link& l,
    const options& opts)
    : _update_interval (.2),
      _expected_bytes (l.length ()),
      _initial_bytes  (f.initial_length ()),
      _time_started (0),
      _last_update (0),
      _term_width (0),
      _dot_count  (0),
      _old_width (0),
      _count (0),
      _width (0),
      _file (f),
      _done (false),
      _mode (normal)
{
    if (_initial_bytes > _expected_bytes)
        _expected_bytes = _initial_bytes;

#ifdef WITH_RESIZE
    signal (SIGWINCH, handle_sigwinch);

    if (!_term_width || recv_sigwinch) {

        _term_width = get_term_width ();

        if (!_term_width)
            _term_width = default_term_width;
    }
#else
    _term_width = default_term_width;
#endif

    _width     = _term_width-1; // Don't use the last column.
    _old_width = _width;

    time (&_time_started);

    const po::variables_map map  = opts.map ();

    if (map.count ("background")) {
        cclive::log << _file.to_s (l) << std::endl;
        _mode = dotline;
    }

    _update_interval = map["update-interval"].as<double>();
}

static double
to_mb (const double bytes) { return bytes/(1024*1024); }

namespace pt = boost::posix_time;

static std::string
to_s (const int secs) {
    pt::time_duration td = pt::seconds (secs);
    return pt::to_simple_string (td);
}

static std::string
to_unit (double& rate) {
    int i = 0;
    if (rate < 1024*1024) {
        rate /= 1024;
    }
    else if (rate < 1024*1024) {
        rate /= 1024*1024;
        i = 1;
    }
    else if (rate < 1024*1024*1024) {
        rate /= 1024*1024*1024;
        i = 2;
    }
    static const char *units[] = {"K/s", "M/s", "G/s"};
    return units[i];
}

namespace fs = boost::filesystem;

void
progressbar::update (double now) {

    time_t tnow;

    time (&tnow);

    const time_t elapsed = tnow - _time_started;

    bool force_update = false;

#ifdef WITH_RESIZE
    if (recv_sigwinch && _mode == normal) {

        const size_t old_term_width = _term_width;

        _term_width = get_term_width ();

        if (!_term_width)
            _term_width = default_term_width;

        if (_term_width != old_term_width) {
            _old_width  = _width;
            _width = _term_width - 1; // Do not use the last column.
            force_update = true;
        }

        recv_sigwinch = 0;
    }
#endif // WITH_RESIZE

    if (!_done) {
        if ((elapsed - _last_update) < _update_interval
            && !force_update)
        {
            return;
        }
    }
    else
        now = _expected_bytes;

    // Current size.

    const double size =
        (!_done)
        ? _initial_bytes + now
        : now;

    std::stringstream size_s;

    size_s.setf (std::ios::fixed);

    size_s
        << std::setprecision (1)
        << to_mb (size)
        << "M";

    // Rate.

    double rate = elapsed ? (now/elapsed):0;

    std::stringstream rate_s, eta_s;

    rate_s.setf (std::ios::fixed);
    eta_s.setf (std::ios::fixed);

    if (rate > 0) {

        // ETA.

        std::string eta;

        if (!_done) {

            const double left =
                (_expected_bytes - (now + _initial_bytes)) / rate;

            eta = to_s (static_cast<int>(left+0.5));
        }
        else {
            rate = (_expected_bytes - _initial_bytes) / elapsed;
            eta  = to_s (elapsed);
        }

        std::string unit = to_unit (rate);

        rate_s
            << std::setw (4)
            << std::setprecision (1)
            << rate
            << unit;

        eta_s
            << std::setw (6)
            << eta;
    }
    else { // ETA: inactive (default).
        rate_s << "--.-K/s";
        eta_s << "--:--:--";
    }

    // Percent.

    std::stringstream percent_s;

    if (_expected_bytes > 0) {

        const int percent =
            static_cast<int>(100.0*size/_expected_bytes);

        if (percent < 100)
            percent_s << std::setw(2) << percent << "%";
        else
            percent_s << "100%";
    }

    // Filename.

    fs::path p = fs::system_complete (_file.path ());

    std::string fname = p.filename ();

    switch (_mode) {
    default:
    case  normal:  _normal  (size_s, rate_s, eta_s, percent_s, fname); break;
    case dotline:  _dotline (size_s, rate_s, eta_s, percent_s, fname); break;
    }

    _last_update = elapsed;
    _count       = now;
}

void
progressbar::_normal (
    const std::stringstream& size_s,
    const std::stringstream& rate_s,
    const std::stringstream& eta_s,
    const std::stringstream& percent_s,
    const std::string& fname)
{
    std::stringstream tmp;

    tmp.setf (std::ios::fixed);

    // Size.

    tmp << "  "
        << std::setw (4)
        << size_s.str ();

    // Rate, ETA.

    tmp << "  "
        << rate_s.str ()
        << "  "
        << eta_s.str ();

    // Percent.

    tmp << "  "
        << percent_s.str ();

    // Filename. Slice and dice.

    const size_t tmp_len = tmp.str ().length ();
    const size_t sub_len = _width - tmp_len;

    std::stringstream b;

    if ((unsigned)sub_len > 0) {

        // Pad to max. terminal width.

        b << fname.substr (0, sub_len);

        while (b.str ().length () < sub_len)  b << " ";

        b << tmp.str ();
    }

    if (_old_width) {

        // Clear line.

        const int m =
            (_old_width < _width)
            ? _width
            : _old_width;

        for (int i=0; i<m; ++i)  cclive::log << " ";

        cclive::log << "\r" << std::flush;

        _old_width = 0;
    }

    // Print.

    cclive::log << b.str () << "\r" << std::flush;
}

void
progressbar::_dotline (
    const std::stringstream& size_s,
    const std::stringstream& rate_s,
    const std::stringstream& eta_s,
    const std::stringstream& percent_s,
    const std::string& fname)
{
    if (++_dot_count >= 31) {
        cclive::log
            << "  "
            << std::setw (6)
            << size_s.str ()
            << "  "
            << rate_s.str ()
            << "  "
            << eta_s.str ()
            << "  "
            << percent_s.str ()
            << std::endl;
        _dot_count = 0;
    }
    else {
        cclive::log << "." << (_dot_count % 3 == 0 ? " ":"") << std::flush;
    }
}

void
progressbar::finish () {

    if (_expected_bytes > 0
        && _count + _initial_bytes > _expected_bytes)
    {
        _expected_bytes = _initial_bytes + _count;
    }

    _done = true;
    update (-1);
}

} // End namespace.

<|endoftext|>


<commit_before>//
//  calcCOVARchunks.cpp
//
//
//  Created by Evan McCartney-Melstad on 2/13/15.
//
//

#include "calcCOVARchunks.h"
#include <iostream>
#include <vector>
#include <fstream>
#include <thread>
#include <string>

int calcCOVARfromBinaryFile (std::string binaryFile, unsigned long long int numLoci, const int numIndividuals, std::string outFile, int lociChunkSize, int numThreads) {

    std::cout << "Number of threads: " << numThreads << std::endl;
    //std::streampos size;
    std::ifstream file (binaryFile, std::ios::in|std::ios::binary);
    std::cout << "Made it to line 22" << std::endl;

    if (file.is_open()) {

        unsigned long long int maxLocus = numLoci;

        std::cout << "Calculating divergence based on " << maxLocus << " total loci." << std::endl;

        // How many bytes to read in at one time (this number of loci will be split amongs numThreads threads, so it should be divisible exactly by numThreads. So the number of loci read in at a time will actually be numLoci*numThreads
        unsigned long long int lociChunkByteSize = (unsigned long long)lociChunkSize * numIndividuals * 2 * numThreads;
        int numFullChunks = (maxLocus*numIndividuals*2)/lociChunkByteSize; // Truncates answer to an integer
        unsigned long long remainingBytesAfterFullChunks = (maxLocus*numIndividuals*2) % lociChunkByteSize;

        std::cout << "Total number of chunks to run: " << numFullChunks + 1 << std::endl;

        /* We are going to split the loci between numThreads threads. Each thread will modify two multidimensional
         vectors that will hold the weightings, the weighted sum of products, and weighted sums of the firsts.
         First, we'll generate all of these vectors, which apparently in C++ needs to be constructed of a
         vector of two-dimensional vectors...
         */
        std::vector<std::vector<std::vector<unsigned long long>>> weightSumProductsThreads(numThreads, std::vector<std::vector<unsigned long long>> (numIndividuals, std::vector<unsigned long long> (numIndividuals,0) ) ); //covarThreads[0] is the first 2D array for the first thread, etc...
        std::vector<std::vector<std::vector<unsigned long long>>> weightSumFirstThreads(numThreads, std::vector<std::vector<unsigned long long>> (numIndividuals, std::vector<unsigned long long> (numIndividuals,0) ) ); //covarThreads[0] is the first 2D array for the first thread, etc...
        std::vector<std::vector<std::vector<unsigned long long int>>> weightingsThreads(numThreads, std::vector<std::vector<unsigned long long int> > (numIndividuals, std::vector<unsigned long long int> (numIndividuals,0) ) );
        std::cout << "Initialized the 3d weighting and covar vectors" << std::endl;

        // Read in the data in chunks, and process each chunk using numThreads threads
        int chunkCounter = 0;
        while (chunkCounter < numFullChunks) {
            unsigned long long bytesPerThread = lociChunkByteSize / numThreads;
            unsigned long long int lociPerThread = bytesPerThread / (numIndividuals*2);

            std::cout << "Running chunk #" << chunkCounter << std::endl;
            std::vector<unsigned char> readCounts(lociChunkByteSize);
            file.read((char*) &readCounts[0], lociChunkByteSize);

            std::cout << "Number of bytes in the chunk vector: " << readCounts.size() << std::endl;

            std::vector<std::thread> threadsVec;
            for (int threadRunning = 0; threadRunning < numThreads; threadRunning++) {

                unsigned long long int firstLocus = (unsigned long long int) threadRunning * lociPerThread;
                unsigned long long int finishingLocus = ((unsigned long long int) threadRunning * lociPerThread) + lociPerThread - (unsigned long long)1.0;

                std::cout << "Got to the function call in main loop. Running thread # " << threadRunning << std::endl;

                //threadsVec.push_back(std::thread(calcCOVARforRange, numIndividuals, lociPerThread, std::ref(readCounts), std::ref(covarThreads[threadRunning]), std::ref(weightingsThreads[threadRunning])));
                threadsVec.push_back(std::thread(calcCOVARforRange, firstLocus, finishingLocus, numIndividuals, std::ref(readCounts), std::ref(weightSumProductsThreads[threadRunning]), std::ref(weightSumFirstThreads[threadRunning]), std::ref(weightingsThreads[threadRunning])));
            }
            // Wait on threads to finish
            for (int i = 0; i < numThreads; ++i) {
                threadsVec[i].join();
                std::cout << "Joined thread " << i << std::endl;
            }
            std::cout << "All threads completed running for chunk " << chunkCounter << " of " << numFullChunks + 1 << std::endl;
            chunkCounter++;
        }

        // For the last chunk, we'll just run it in a single thread, so we don't have to worry about numRemainingLoci/lociPerThread remainders... Just add everything to the vectors for thread 1
        std::vector<unsigned char> readCountsRemaining(remainingBytesAfterFullChunks);
        file.read((char*) &readCountsRemaining[0], remainingBytesAfterFullChunks);
        unsigned long long int finishingLocus = (readCountsRemaining.size()/(numIndividuals*2)) - 1;
        calcCOVARforRange(0, finishingLocus, numIndividuals, std::ref(readCountsRemaining), std::ref(weightSumProductsThreads[0]), std::ref(weightSumFirstThreads[0]), std::ref(weightingsThreads[0]));

        // Now aggregate the results of the threads and print final results
        std::vector<std::vector<long double>> weightingsSUM(numIndividuals, std::vector<long double>(numIndividuals,0));
        std::vector<std::vector<long double>> weightSumProductsSUM(numIndividuals, std::vector<long double>(numIndividuals,0));
        std::vector<std::vector<long double>> weightSumFirstSUM(numIndividuals, std::vector<long double>(numIndividuals,0));

        for (int tortoise = 0; tortoise < numIndividuals; tortoise++) {
            for (int comparisonTortoise = 0; comparisonTortoise <= tortoise; comparisonTortoise++) {
                for (int threadVector = 0; threadVector < numThreads; threadVector++) {
                    weightingsSUM[tortoise][comparisonTortoise] += weightingsThreads[threadVector][tortoise][comparisonTortoise];
                    weightSumProductsSUM[tortoise][comparisonTortoise] += weightSumProductsThreads[threadVector][tortoise][comparisonTortoise];
                }
            }
        }

        //weightSumFirstSUM is a full matrix (not half), so run comparison tortoise from 0 to numIndividuals, not 0 to tortoise
        for (int tortoise = 0; tortoise < numIndividuals; tortoise++) {
            for (int comparisonTortoise = 0; comparisonTortoise < numIndividuals; comparisonTortoise++) {
                for (int threadVector = 0; threadVector < numThreads; threadVector++) {
                    weightSumFirstSUM[tortoise][comparisonTortoise] += weightSumFirstThreads[threadVector][tortoise][comparisonTortoise];
                }
            }
        }

        std::cout << "Finished summing the threads vectors" << std::endl;\
        file.close(); // This is the binary file that holds all the read count data

        // Now print out the final output to the pairwise covariance file:
        std::ofstream covarOUT (outFile);
        if (!covarOUT) {
            std::cerr << "Crap, " << outFile << "didn't open!" << std::endl;
        } else {
            for (int tortoise=0; tortoise < numIndividuals; tortoise++) {
                for (int comparisonTortoise = 0; comparisonTortoise <= tortoise; comparisonTortoise++) {

                    covar = long double(((weightSumProductsSUM[tortoise][comparisonTortoise])/(weightingsSUM[tortoise][comparisonTortoise])) - ((weightSumFirstSUM[tortoise][comparisonTortoise])/(weightingsSUM[tortoise][comparisonTortoise]))*((weightSumFirstSUM[comparisonTortoise][tortoise])/weightingsSUM[tortoise][comparisonTortoise]));
                    covarOUT << covar << std::endl;
                }
            }
        }
    } else std::cout << "Unable to open file";

    return 0;
}

/*
 D[N] =  total coverage
 P[N] = major allele counts
 W[N,N] = weights
 C[N,N] = weighted sum of products
 Z[N,N] = weighted sum of first one (note cancelation of D[N])
 W[N,M] = weights
 C[N,M] = weighted sum of products
 Z[N,M] = weighted sum of first one (note cancelation of D[M]) : weightSumFirst
 Z[M,N] = weighted sum of second one (IN TRANSPOSE)            : weightSumFirst
 */

int calcCOVARforRange (unsigned long long startingLocus, unsigned long long endingLocus, int numIndividuals, std::vector<unsigned char>& mainReadCountVector, std::vector<std::vector<unsigned long long int>>& weightSumProducts, std::vector<std::vector<unsigned long long int>>& weightSumFirst, std::vector<std::vector<unsigned long long int>>& threadWeightings) {

    std::cout << "Calculating COVAR for the following locus range: " << startingLocus << " to " << endingLocus << std::endl;

    for( unsigned long long locus = startingLocus; locus < endingLocus; locus++) {
        if (locus % 100000 == 0) {
            std::cout << locus << " loci processed through calcCOVARfromBinaryFile" << std::endl;
        }

        unsigned long long coverages[numIndividuals];
        unsigned long long int *majorAlleleCounts = new unsigned long long int[numIndividuals]; // This will hold the major allele counts for that locus for each tortoise

        for( int tortoise = 0; tortoise < numIndividuals; tortoise++ ) {
            unsigned long long majorIndex = locus * (numIndividuals*2) + 2 * tortoise;
            unsigned long long minorIndex = locus * (numIndividuals*2) + 2 * tortoise + 1;
            coverages[tortoise] = int(mainReadCountVector[majorIndex]) + int(mainReadCountVector[minorIndex]); // Hold the coverages for each locus

            if ( coverages[tortoise] > 0 ) {
                majorAlleleCounts[tortoise] = (unsigned long long int)mainReadCountVector[majorIndex]; // This will be an integer value

                if (coverages[tortoise] > 1) {

                    unsigned long long int locusWeighting = (unsigned long long int) (coverages[tortoise]*(coverages[tortoise]-1));
                    threadWeightings[tortoise][tortoise] += (unsigned long long int)locusWeighting; // This is an integer--discrete number of reads

                    weightSumProducts[tortoise][tortoise] += (majorAlleleCounts[tortoise]) * (majorAlleleCounts[tortoise]) * (coverages[tortoise]-1)/(coverages[tortoise]);
                    weightSumFirst[tortoise][tortoise] += (majorAlleleCounts[tortoise]) * (coverages[tortoise]-1);

                }

                for( int comparisonTortoise = 0; comparisonTortoise < tortoise; comparisonTortoise++) {
                    if (coverages[comparisonTortoise] > 0) {
                        unsigned long long int locusWeighting = (unsigned long long int)coverages[tortoise] * (unsigned long long int)(coverages[comparisonTortoise]);
                        threadWeightings[tortoise][comparisonTortoise] += locusWeighting;

                        weightSumProducts[tortoise][comparisonTortoise] += (majorAlleleCounts[tortoise]) * (majorAlleleCounts[comparisonTortoise]);
                        weightSumFirst[tortoise][comparisonTortoise] += (majorAlleleCounts[comparisonTortoise]) * coverages[tortoise];
                        weightSumFirst[comparisonTortoise][tortoise] += (majorAlleleCounts[tortoise]) * coverages[comparisonTortoise];
                    }
                }
            }
        }
        delete[] majorAlleleCounts; // Needed to avoid memory leaks
    }
    std::cout << "Finished thread ending on locus " << endingLocus << std::endl;
    return 0;
}

<commit_msg>Minor change<commit_after>//
//  calcCOVARchunks.cpp
//
//
//  Created by Evan McCartney-Melstad on 2/13/15.
//
//

#include "calcCOVARchunks.h"
#include <iostream>
#include <vector>
#include <fstream>
#include <thread>
#include <string>

int calcCOVARfromBinaryFile (std::string binaryFile, unsigned long long int numLoci, const int numIndividuals, std::string outFile, int lociChunkSize, int numThreads) {

    std::cout << "Number of threads: " << numThreads << std::endl;
    //std::streampos size;
    std::ifstream file (binaryFile, std::ios::in|std::ios::binary);
    std::cout << "Made it to line 22" << std::endl;

    if (file.is_open()) {

        unsigned long long int maxLocus = numLoci;

        std::cout << "Calculating divergence based on " << maxLocus << " total loci." << std::endl;

        // How many bytes to read in at one time (this number of loci will be split amongs numThreads threads, so it should be divisible exactly by numThreads. So the number of loci read in at a time will actually be numLoci*numThreads
        unsigned long long int lociChunkByteSize = (unsigned long long)lociChunkSize * numIndividuals * 2 * numThreads;
        int numFullChunks = (maxLocus*numIndividuals*2)/lociChunkByteSize; // Truncates answer to an integer
        unsigned long long remainingBytesAfterFullChunks = (maxLocus*numIndividuals*2) % lociChunkByteSize;

        std::cout << "Total number of chunks to run: " << numFullChunks + 1 << std::endl;

        /* We are going to split the loci between numThreads threads. Each thread will modify two multidimensional
         vectors that will hold the weightings, the weighted sum of products, and weighted sums of the firsts.
         First, we'll generate all of these vectors, which apparently in C++ needs to be constructed of a
         vector of two-dimensional vectors...
         */
        std::vector<std::vector<std::vector<unsigned long long>>> weightSumProductsThreads(numThreads, std::vector<std::vector<unsigned long long>> (numIndividuals, std::vector<unsigned long long> (numIndividuals,0) ) ); //covarThreads[0] is the first 2D array for the first thread, etc...
        std::vector<std::vector<std::vector<unsigned long long>>> weightSumFirstThreads(numThreads, std::vector<std::vector<unsigned long long>> (numIndividuals, std::vector<unsigned long long> (numIndividuals,0) ) ); //covarThreads[0] is the first 2D array for the first thread, etc...
        std::vector<std::vector<std::vector<unsigned long long int>>> weightingsThreads(numThreads, std::vector<std::vector<unsigned long long int> > (numIndividuals, std::vector<unsigned long long int> (numIndividuals,0) ) );
        std::cout << "Initialized the 3d weighting and covar vectors" << std::endl;

        // Read in the data in chunks, and process each chunk using numThreads threads
        int chunkCounter = 0;
        while (chunkCounter < numFullChunks) {
            unsigned long long bytesPerThread = lociChunkByteSize / numThreads;
            unsigned long long int lociPerThread = bytesPerThread / (numIndividuals*2);

            std::cout << "Running chunk #" << chunkCounter << std::endl;
            std::vector<unsigned char> readCounts(lociChunkByteSize);
            file.read((char*) &readCounts[0], lociChunkByteSize);

            std::cout << "Number of bytes in the chunk vector: " << readCounts.size() << std::endl;

            std::vector<std::thread> threadsVec;
            for (int threadRunning = 0; threadRunning < numThreads; threadRunning++) {

                unsigned long long int firstLocus = (unsigned long long int) threadRunning * lociPerThread;
                unsigned long long int finishingLocus = ((unsigned long long int) threadRunning * lociPerThread) + lociPerThread - (unsigned long long)1.0;

                std::cout << "Got to the function call in main loop. Running thread # " << threadRunning << std::endl;

                //threadsVec.push_back(std::thread(calcCOVARforRange, numIndividuals, lociPerThread, std::ref(readCounts), std::ref(covarThreads[threadRunning]), std::ref(weightingsThreads[threadRunning])));
                threadsVec.push_back(std::thread(calcCOVARforRange, firstLocus, finishingLocus, numIndividuals, std::ref(readCounts), std::ref(weightSumProductsThreads[threadRunning]), std::ref(weightSumFirstThreads[threadRunning]), std::ref(weightingsThreads[threadRunning])));
            }
            // Wait on threads to finish
            for (int i = 0; i < numThreads; ++i) {
                threadsVec[i].join();
                std::cout << "Joined thread " << i << std::endl;
            }
            std::cout << "All threads completed running for chunk " << chunkCounter << " of " << numFullChunks + 1 << std::endl;
            chunkCounter++;
        }

        // For the last chunk, we'll just run it in a single thread, so we don't have to worry about numRemainingLoci/lociPerThread remainders... Just add everything to the vectors for thread 1
        std::vector<unsigned char> readCountsRemaining(remainingBytesAfterFullChunks);
        file.read((char*) &readCountsRemaining[0], remainingBytesAfterFullChunks);
        unsigned long long int finishingLocus = (readCountsRemaining.size()/(numIndividuals*2)) - 1;
        calcCOVARforRange(0, finishingLocus, numIndividuals, std::ref(readCountsRemaining), std::ref(weightSumProductsThreads[0]), std::ref(weightSumFirstThreads[0]), std::ref(weightingsThreads[0]));

        // Now aggregate the results of the threads and print final results
        std::vector<std::vector<long double>> weightingsSUM(numIndividuals, std::vector<long double>(numIndividuals,0));
        std::vector<std::vector<long double>> weightSumProductsSUM(numIndividuals, std::vector<long double>(numIndividuals,0));
        std::vector<std::vector<long double>> weightSumFirstSUM(numIndividuals, std::vector<long double>(numIndividuals,0));

        for (int tortoise = 0; tortoise < numIndividuals; tortoise++) {
            for (int comparisonTortoise = 0; comparisonTortoise <= tortoise; comparisonTortoise++) {
                for (int threadVector = 0; threadVector < numThreads; threadVector++) {
                    weightingsSUM[tortoise][comparisonTortoise] += weightingsThreads[threadVector][tortoise][comparisonTortoise];
                    weightSumProductsSUM[tortoise][comparisonTortoise] += weightSumProductsThreads[threadVector][tortoise][comparisonTortoise];
                }
            }
        }

        //weightSumFirstSUM is a full matrix (not half), so run comparison tortoise from 0 to numIndividuals, not 0 to tortoise
        for (int tortoise = 0; tortoise < numIndividuals; tortoise++) {
            for (int comparisonTortoise = 0; comparisonTortoise < numIndividuals; comparisonTortoise++) {
                for (int threadVector = 0; threadVector < numThreads; threadVector++) {
                    weightSumFirstSUM[tortoise][comparisonTortoise] += weightSumFirstThreads[threadVector][tortoise][comparisonTortoise];
                }
            }
        }

        std::cout << "Finished summing the threads vectors" << std::endl;\
        file.close(); // This is the binary file that holds all the read count data

        // Now print out the final output to the pairwise covariance file:
        std::ofstream covarOUT (outFile);
        if (!covarOUT) {
            std::cerr << "Crap, " << outFile << "didn't open!" << std::endl;
        } else {
            for (int tortoise=0; tortoise < numIndividuals; tortoise++) {
                for (int comparisonTortoise = 0; comparisonTortoise <= tortoise; comparisonTortoise++) {

                    long double covar = long double(((weightSumProductsSUM[tortoise][comparisonTortoise])/(weightingsSUM[tortoise][comparisonTortoise])) - ((weightSumFirstSUM[tortoise][comparisonTortoise])/(weightingsSUM[tortoise][comparisonTortoise]))*((weightSumFirstSUM[comparisonTortoise][tortoise])/weightingsSUM[tortoise][comparisonTortoise]));
                    covarOUT << covar << std::endl;
                }
            }
        }
    } else std::cout << "Unable to open file";

    return 0;
}

/*
 D[N] =  total coverage
 P[N] = major allele counts
 W[N,N] = weights
 C[N,N] = weighted sum of products
 Z[N,N] = weighted sum of first one (note cancelation of D[N])
 W[N,M] = weights
 C[N,M] = weighted sum of products
 Z[N,M] = weighted sum of first one (note cancelation of D[M]) : weightSumFirst
 Z[M,N] = weighted sum of second one (IN TRANSPOSE)            : weightSumFirst
 */

int calcCOVARforRange (unsigned long long startingLocus, unsigned long long endingLocus, int numIndividuals, std::vector<unsigned char>& mainReadCountVector, std::vector<std::vector<unsigned long long int>>& weightSumProducts, std::vector<std::vector<unsigned long long int>>& weightSumFirst, std::vector<std::vector<unsigned long long int>>& threadWeightings) {

    std::cout << "Calculating COVAR for the following locus range: " << startingLocus << " to " << endingLocus << std::endl;

    for( unsigned long long locus = startingLocus; locus < endingLocus; locus++) {
        if (locus % 100000 == 0) {
            std::cout << locus << " loci processed through calcCOVARfromBinaryFile" << std::endl;
        }

        unsigned long long coverages[numIndividuals];
        unsigned long long int *majorAlleleCounts = new unsigned long long int[numIndividuals]; // This will hold the major allele counts for that locus for each tortoise

        for( int tortoise = 0; tortoise < numIndividuals; tortoise++ ) {
            unsigned long long majorIndex = locus * (numIndividuals*2) + 2 * tortoise;
            unsigned long long minorIndex = locus * (numIndividuals*2) + 2 * tortoise + 1;
            coverages[tortoise] = int(mainReadCountVector[majorIndex]) + int(mainReadCountVector[minorIndex]); // Hold the coverages for each locus

            if ( coverages[tortoise] > 0 ) {
                majorAlleleCounts[tortoise] = (unsigned long long int)mainReadCountVector[majorIndex]; // This will be an integer value

                if (coverages[tortoise] > 1) {

                    unsigned long long int locusWeighting = (unsigned long long int) (coverages[tortoise]*(coverages[tortoise]-1));
                    threadWeightings[tortoise][tortoise] += (unsigned long long int)locusWeighting; // This is an integer--discrete number of reads

                    weightSumProducts[tortoise][tortoise] += (majorAlleleCounts[tortoise]) * (majorAlleleCounts[tortoise]) * (coverages[tortoise]-1)/(coverages[tortoise]);
                    weightSumFirst[tortoise][tortoise] += (majorAlleleCounts[tortoise]) * (coverages[tortoise]-1);

                }

                for( int comparisonTortoise = 0; comparisonTortoise < tortoise; comparisonTortoise++) {
                    if (coverages[comparisonTortoise] > 0) {
                        unsigned long long int locusWeighting = (unsigned long long int)coverages[tortoise] * (unsigned long long int)(coverages[comparisonTortoise]);
                        threadWeightings[tortoise][comparisonTortoise] += locusWeighting;

                        weightSumProducts[tortoise][comparisonTortoise] += (majorAlleleCounts[tortoise]) * (majorAlleleCounts[comparisonTortoise]);
                        weightSumFirst[tortoise][comparisonTortoise] += (majorAlleleCounts[comparisonTortoise]) * coverages[tortoise];
                        weightSumFirst[comparisonTortoise][tortoise] += (majorAlleleCounts[tortoise]) * coverages[comparisonTortoise];
                    }
                }
            }
        }
        delete[] majorAlleleCounts; // Needed to avoid memory leaks
    }
    std::cout << "Finished thread ending on locus " << endingLocus << std::endl;
    return 0;
}

<|endoftext|>


<commit_before>#include "SpecializeClampedRamps.h"
#include "IRMutator.h"
#include "IROperator.h"
#include "Simplify.h"
#include "Substitute.h"

namespace Halide {
namespace Internal {

namespace {
class PredicateFinder : public IRMutator {
public:
    Expr min_predicate, max_predicate;
    PredicateFinder() : min_predicate(const_true()), max_predicate(const_true()) {}

private:
    using IRVisitor::visit;

    void visit(const Min *op) {
        Expr a = simplify(mutate(op->a));
        Expr b = simplify(mutate(op->b));

        const Ramp *ra = a.as<Ramp>();
        const Ramp *rb = b.as<Ramp>();
        const Broadcast *ba = a.as<Broadcast>();
        const Broadcast *bb = b.as<Broadcast>();

        if (rb && ba) {
            std::swap(a, b);
            std::swap(ra, rb);
            std::swap(ba, bb);
        }

        if (ra && bb) {
            Expr max_a = ra->base + ra->stride * (ra->width - 1);
            Expr min_b = bb->value;
            min_predicate = min_predicate && (max_a <= min_b);
            expr = a;
        } else if (a.same_as(op->a) && b.same_as(op->b)) {
            expr = op;
        } else {
            expr = Min::make(a, b);
        }
    }

    void visit(const Max *op) {
        Expr a = simplify(mutate(op->a));
        Expr b = simplify(mutate(op->b));

        const Ramp *ra = a.as<Ramp>();
        const Ramp *rb = b.as<Ramp>();
        const Broadcast *ba = a.as<Broadcast>();
        const Broadcast *bb = b.as<Broadcast>();

        if (rb && ba) {
            std::swap(a, b);
            std::swap(ra, rb);
            std::swap(ba, bb);
        }

        if (ra && bb) {
            Expr min_a = ra->base;
            Expr max_b = bb->value;
            max_predicate = max_predicate && (min_a >= max_b);
            expr = a;
        } else if (a.same_as(op->a) && b.same_as(op->b)) {
            expr = op;
        } else {
            expr = Max::make(a, b);
        }
    }

    void visit(const Let *op) {
        Expr value = mutate(op->value);
        Expr body = mutate(op->body);

        if (value.same_as(op->value) && body.same_as(op->body)) {
            expr = op;
        } else {
            expr = Let::make(op->name, op->value, op->body);
        }

        min_predicate = substitute(op->name, value, min_predicate);
        max_predicate = substitute(op->name, value, max_predicate);
    }
};

class SpecializeClampedRamps : public IRMutator {
    using IRMutator::visit;

    void visit(const Store *op) {
        PredicateFinder p;
        Stmt simpler_store = p.mutate(op);
        if (simpler_store.same_as(op)) {
            stmt = op;
        } else {
            Expr predicate = simplify(p.min_predicate && p.max_predicate);
            stmt = IfThenElse::make(predicate, simpler_store, op);
        }
    }

    void visit(const LetStmt *op) {
        PredicateFinder p;
        Stmt body = mutate(op->body);
        Expr simpler_value = p.mutate(op->value);
        if (body.same_as(op->body) && simpler_value.same_as(op->value)) {
            stmt = op;
        } else if (simpler_value.same_as(op->value)) {
            stmt = LetStmt::make(op->name, op->value, body);
        } else {
            Stmt simpler_let = LetStmt::make(op->name, simpler_value, body);
            Expr predicate = simplify(p.min_predicate && p.max_predicate);
            stmt = IfThenElse::make(predicate, simpler_let, op);
        }
    }
};
}

Stmt specialize_clamped_ramps(Stmt s) {
    return SpecializeClampedRamps().mutate(s);
}

}
}
<commit_msg>Fix clamped ramps bug.<commit_after>#include "SpecializeClampedRamps.h"
#include "IRMutator.h"
#include "IROperator.h"
#include "Simplify.h"
#include "Substitute.h"

namespace Halide {
namespace Internal {

namespace {
class PredicateFinder : public IRMutator {
public:
    Expr min_predicate, max_predicate;
    PredicateFinder() : min_predicate(const_true()), max_predicate(const_true()) {}

private:
    using IRVisitor::visit;

    void visit(const Min *op) {
        Expr a = simplify(mutate(op->a));
        Expr b = simplify(mutate(op->b));

        const Ramp *ra = a.as<Ramp>();
        const Ramp *rb = b.as<Ramp>();
        const Broadcast *ba = a.as<Broadcast>();
        const Broadcast *bb = b.as<Broadcast>();

        if (rb && ba) {
            std::swap(a, b);
            std::swap(ra, rb);
            std::swap(ba, bb);
        }

        if (ra && bb) {
            Expr max_a = ra->base + ra->stride * (ra->width - 1);
            Expr min_b = bb->value;
            min_predicate = min_predicate && (max_a <= min_b);
            expr = a;
        } else if (a.same_as(op->a) && b.same_as(op->b)) {
            expr = op;
        } else {
            expr = Min::make(a, b);
        }
    }

    void visit(const Max *op) {
        Expr a = simplify(mutate(op->a));
        Expr b = simplify(mutate(op->b));

        const Ramp *ra = a.as<Ramp>();
        const Ramp *rb = b.as<Ramp>();
        const Broadcast *ba = a.as<Broadcast>();
        const Broadcast *bb = b.as<Broadcast>();

        if (rb && ba) {
            std::swap(a, b);
            std::swap(ra, rb);
            std::swap(ba, bb);
        }

        if (ra && bb) {
            Expr min_a = ra->base;
            Expr max_b = bb->value;
            max_predicate = max_predicate && (min_a >= max_b);
            expr = a;
        } else if (a.same_as(op->a) && b.same_as(op->b)) {
            expr = op;
        } else {
            expr = Max::make(a, b);
        }
    }

    void visit(const Let *op) {
        Expr value = mutate(op->value);
        Expr body = mutate(op->body);

        if (value.same_as(op->value) && body.same_as(op->body)) {
            expr = op;
        } else {
            expr = Let::make(op->name, value, body);
        }

        min_predicate = substitute(op->name, value, min_predicate);
        max_predicate = substitute(op->name, value, max_predicate);
    }
};

class SpecializeClampedRamps : public IRMutator {
    using IRMutator::visit;

    void visit(const Store *op) {
        PredicateFinder p;
        Stmt simpler_store = p.mutate(op);
        if (simpler_store.same_as(op)) {
            stmt = op;
        } else {
            Expr predicate = simplify(p.min_predicate && p.max_predicate);
            stmt = IfThenElse::make(predicate, simpler_store, op);
        }
    }

    void visit(const LetStmt *op) {
        PredicateFinder p;
        Stmt body = mutate(op->body);
        Expr simpler_value = p.mutate(op->value);
        if (body.same_as(op->body) && simpler_value.same_as(op->value)) {
            stmt = op;
        } else if (simpler_value.same_as(op->value)) {
            stmt = LetStmt::make(op->name, op->value, body);
        } else {
            Stmt simpler_let = LetStmt::make(op->name, simpler_value, body);
            Expr predicate = simplify(p.min_predicate && p.max_predicate);
            stmt = IfThenElse::make(predicate, simpler_let, op);
        }
    }
};
}

Stmt specialize_clamped_ramps(Stmt s) {
    return SpecializeClampedRamps().mutate(s);
}

}
}
<|endoftext|>


<commit_before>// Copyright 2004-present Facebook. All Rights Reserved.

#include "src/StandardReactiveSocket.h"

#include <folly/ExceptionWrapper.h>
#include <folly/Memory.h>
#include <folly/MoveWrapper.h>
#include <folly/io/async/EventBase.h>
#include "src/ClientResumeStatusCallback.h"
#include "src/ConnectionAutomaton.h"
#include "src/FrameTransport.h"
#include "src/RequestHandler.h"
#include "src/automata/ChannelResponder.h"

namespace reactivesocket {

StandardReactiveSocket::~StandardReactiveSocket() {
  debugCheckCorrectExecutor();

  // Force connection closure, this will trigger terminal signals to be
  // delivered to all stream automata.
  close();
}

StandardReactiveSocket::StandardReactiveSocket(
    ReactiveSocketMode mode,
    std::shared_ptr<RequestHandler> handler,
    Stats& stats,
    std::unique_ptr<KeepaliveTimer> keepaliveTimer,
    folly::Executor& executor)
    : handler_(handler),
      connection_(std::make_shared<ConnectionAutomaton>(
          executor,
          [this, handler](
              ConnectionAutomaton& connection,
              StreamId streamId,
              std::unique_ptr<folly::IOBuf> serializedFrame) {
            createResponder(
                handler, connection, streamId, std::move(serializedFrame));
          },
          std::make_shared<StreamState>(stats),
          handler,
          std::bind(
              &StandardReactiveSocket::resumeListener,
              this,
              std::placeholders::_1),
          stats,
          std::move(keepaliveTimer),
          mode)),
      streamsFactory_(connection_, mode),
      executor_(executor) {
  debugCheckCorrectExecutor();
  stats.socketCreated();
}

std::unique_ptr<StandardReactiveSocket>
StandardReactiveSocket::fromClientConnection(
    folly::Executor& executor,
    std::unique_ptr<DuplexConnection> connection,
    std::unique_ptr<RequestHandler> handler,
    ConnectionSetupPayload setupPayload,
    Stats& stats,
    std::unique_ptr<KeepaliveTimer> keepaliveTimer) {
  auto socket = disconnectedClient(
      executor, std::move(handler), stats, std::move(keepaliveTimer));
  socket->clientConnect(
      std::make_shared<FrameTransport>(std::move(connection)),
      std::move(setupPayload));
  return socket;
}

std::unique_ptr<StandardReactiveSocket>
StandardReactiveSocket::disconnectedClient(
    folly::Executor& executor,
    std::unique_ptr<RequestHandler> handler,
    Stats& stats,
    std::unique_ptr<KeepaliveTimer> keepaliveTimer) {
  std::unique_ptr<StandardReactiveSocket> socket(new StandardReactiveSocket(
      ReactiveSocketMode::CLIENT,
      std::move(handler),
      stats,
      std::move(keepaliveTimer),
      executor));
  return socket;
}

std::unique_ptr<StandardReactiveSocket>
StandardReactiveSocket::fromServerConnection(
    folly::Executor& executor,
    std::unique_ptr<DuplexConnection> connection,
    std::unique_ptr<RequestHandler> handler,
    Stats& stats,
    bool isResumable) {
  // TODO: isResumable should come as a flag on Setup frame and it should be
  // exposed to the application code. We should then remove this parameter
  auto socket = disconnectedServer(executor, std::move(handler), stats);
  socket->serverConnect(
      std::make_shared<FrameTransport>(std::move(connection)), isResumable);
  return socket;
}

std::unique_ptr<StandardReactiveSocket>
StandardReactiveSocket::disconnectedServer(
    folly::Executor& executor,
    std::unique_ptr<RequestHandler> handler,
    Stats& stats) {
  std::unique_ptr<StandardReactiveSocket> socket(new StandardReactiveSocket(
      ReactiveSocketMode::SERVER,
      std::move(handler),
      stats,
      nullptr,
      executor));
  return socket;
}

std::shared_ptr<Subscriber<Payload>> StandardReactiveSocket::requestChannel(
    std::shared_ptr<Subscriber<Payload>> responseSink) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  return streamsFactory_.createChannelRequester(
      std::move(responseSink), executor_);
}

void StandardReactiveSocket::requestStream(
    Payload request,
    std::shared_ptr<Subscriber<Payload>> responseSink) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  streamsFactory_.createStreamRequester(
      std::move(request), std::move(responseSink), executor_);
}

void StandardReactiveSocket::requestSubscription(
    Payload request,
    std::shared_ptr<Subscriber<Payload>> responseSink) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  streamsFactory_.createSubscriptionRequester(
      std::move(request), std::move(responseSink), executor_);
}

void StandardReactiveSocket::requestResponse(
    Payload payload,
    std::shared_ptr<Subscriber<Payload>> responseSink) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  streamsFactory_.createRequestResponseRequester(
      std::move(payload), std::move(responseSink), executor_);
}

void StandardReactiveSocket::requestFireAndForget(Payload request) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  Frame_REQUEST_FNF frame(
      streamsFactory_.getNextStreamId(),
      FrameFlags_EMPTY,
      std::move(std::move(request)));
  connection_->outputFrameOrEnqueue(frame.serializeOut());
}

void StandardReactiveSocket::metadataPush(
    std::unique_ptr<folly::IOBuf> metadata) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  connection_->outputFrameOrEnqueue(
      Frame_METADATA_PUSH(std::move(metadata)).serializeOut());
}

void StandardReactiveSocket::createResponder(
    std::shared_ptr<RequestHandler> handler,
    ConnectionAutomaton& connection,
    StreamId streamId,
    std::unique_ptr<folly::IOBuf> serializedFrame) {
  debugCheckCorrectExecutor();

  if (streamId != 0 && !streamsFactory_.registerNewPeerStreamId(streamId)) {
    return;
  }

  auto type = FrameHeader::peekType(*serializedFrame);
  switch (type) {
    case FrameType::SETUP: {
      Frame_SETUP frame;
      if (!connection.deserializeFrameOrError(
              frame, std::move(serializedFrame))) {
        return;
      }
      if (frame.header_.flags_ & FrameFlags_LEASE) {
        // TODO(yschimke) We don't have the correct lease and wait logic above
        // yet
        LOG(WARNING) << "ignoring setup frame with lease";
        //          connectionOutput_.onNext(
        //              Frame_ERROR::badSetupFrame("leases not supported")
        //                  .serializeOut());
        //          disconnect();
      }

      auto streamState = handler->handleSetupPayload(
          *this,
          ConnectionSetupPayload(
              std::move(frame.metadataMimeType_),
              std::move(frame.dataMimeType_),
              std::move(frame.payload_),
              false, // TODO: resumable flag should be received in SETUP frame
              frame.token_));

      // TODO(lehecka): use again
      // connection.useStreamState(streamState);
      break;
    }
    case FrameType::RESUME: {
      Frame_RESUME frame;
      if (!connection.deserializeFrameOrError(
              frame, std::move(serializedFrame))) {
        return;
      }
      auto resumed =
          handler->handleResume(*this, frame.token_, frame.position_);
      if (!resumed) {
        // TODO(lehecka): the "connection" and "this" arguments needs to be
        // cleaned up. It is not intuitive what is their lifetime.
        auto connectionCopy = std::move(connection_);
        connection.closeWithError(
            Frame_ERROR::connectionError("can not resume"));
      }
      break;
    }
    case FrameType::REQUEST_CHANNEL: {
      Frame_REQUEST_CHANNEL frame;
      if (!connection.deserializeFrameOrError(
              frame, std::move(serializedFrame))) {
        return;
      }
      auto automaton = streamsFactory_.createChannelResponder(
          frame.requestN_, streamId, executor_);
      auto requestSink = handler->handleRequestChannel(
          std::move(frame.payload_), streamId, automaton);
      automaton->subscribe(requestSink);
      break;
    }
    case FrameType::REQUEST_STREAM: {
      Frame_REQUEST_STREAM frame;
      if (!connection.deserializeFrameOrError(
              frame, std::move(serializedFrame))) {
        return;
      }
      auto automaton = streamsFactory_.createStreamResponder(
          frame.requestN_, streamId, executor_);
      handler->handleRequestStream(
          std::move(frame.payload_), streamId, automaton);
      break;
    }
    case FrameType::REQUEST_SUB: {
      Frame_REQUEST_SUB frame;
      if (!connection.deserializeFrameOrError(
              frame, std::move(serializedFrame))) {
        return;
      }
      auto automaton = streamsFactory_.createSubscriptionResponder(
          frame.requestN_, streamId, executor_);
      handler->handleRequestSubscription(
          std::move(frame.payload_), streamId, automaton);
      break;
    }
    case FrameType::REQUEST_RESPONSE: {
      Frame_REQUEST_RESPONSE frame;
      if (!connection.deserializeFrameOrError(
              frame, std::move(serializedFrame))) {
        return;
      }
      auto automaton =
          streamsFactory_.createRequestResponseResponder(streamId, executor_);
      handler->handleRequestResponse(
          std::move(frame.payload_), streamId, automaton);
      break;
    }
    case FrameType::REQUEST_FNF: {
      Frame_REQUEST_FNF frame;
      if (!connection.deserializeFrameOrError(
              frame, std::move(serializedFrame))) {
        return;
      }
      // no stream tracking is necessary
      handler->handleFireAndForgetRequest(std::move(frame.payload_), streamId);
      break;
    }
    case FrameType::METADATA_PUSH: {
      Frame_METADATA_PUSH frame;
      if (!connection.deserializeFrameOrError(
              frame, std::move(serializedFrame))) {
        return;
      }
      handler->handleMetadataPush(std::move(frame.metadata_));
      break;
    }
    // Other frames cannot start a stream.

    case FrameType::LEASE:
    case FrameType::KEEPALIVE:
    case FrameType::RESERVED:
    case FrameType::REQUEST_N:
    case FrameType::CANCEL:
    case FrameType::RESPONSE:
    case FrameType::ERROR:
    case FrameType::RESUME_OK:
    default:
      // TODO(lehecka): the "connection" and "this" arguments needs to be
      // cleaned up. It is not intuitive what is their lifetime.
      auto connectionCopy = std::move(connection_);
      connection.closeWithError(Frame_ERROR::unexpectedFrame());
  }
}

std::shared_ptr<StreamState> StandardReactiveSocket::resumeListener(
    const ResumeIdentificationToken& token) {
  debugCheckCorrectExecutor();
  CHECK(false) << "not implemented";
  // TODO(lehecka)
  return nullptr;
  //  return handler_->handleResume(token);
}

void StandardReactiveSocket::clientConnect(
    std::shared_ptr<FrameTransport> frameTransport,
    ConnectionSetupPayload setupPayload) {
  CHECK(frameTransport && !frameTransport->isClosed());
  debugCheckCorrectExecutor();
  checkNotClosed();
  connection_->setResumable(setupPayload.resumable);

  // TODO set correct version
  Frame_SETUP frame(
      setupPayload.resumable ? FrameFlags_RESUME_ENABLE : FrameFlags_EMPTY,
      /*version=*/0,
      connection_->getKeepaliveTime(),
      std::numeric_limits<uint32_t>::max(),
      setupPayload.token,
      std::move(setupPayload.metadataMimeType),
      std::move(setupPayload.dataMimeType),
      std::move(setupPayload.payload));

  // TODO: when the server returns back that it doesn't support resumability, we
  // should retry without resumability

  // making sure we send setup frame first
  frameTransport->outputFrameOrEnqueue(frame.serializeOut());
  // then the rest of the cached frames will be sent
  connection_->connect(std::move(frameTransport), true);
}

void StandardReactiveSocket::serverConnect(
    std::shared_ptr<FrameTransport> frameTransport,
    bool isResumable) {
  debugCheckCorrectExecutor();
  connection_->setResumable(isResumable);
  connection_->connect(std::move(frameTransport), true);
}

void StandardReactiveSocket::close() {
  debugCheckCorrectExecutor();
  if (auto connectionCopy = std::move(connection_)) {
    connectionCopy->close(
        folly::exception_wrapper(), StreamCompletionSignal::SOCKET_CLOSED);
  }
}

void StandardReactiveSocket::disconnect() {
  debugCheckCorrectExecutor();
  checkNotClosed();
  connection_->disconnect(folly::exception_wrapper());
}

std::shared_ptr<FrameTransport> StandardReactiveSocket::detachFrameTransport() {
  debugCheckCorrectExecutor();
  checkNotClosed();
  return connection_->detachFrameTransport();
}

void StandardReactiveSocket::onConnected(std::function<void()> listener) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  connection_->addConnectedListener(std::move(listener));
}

void StandardReactiveSocket::onDisconnected(ErrorCallback listener) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  connection_->addDisconnectedListener(std::move(listener));
}

void StandardReactiveSocket::onClosed(ErrorCallback listener) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  connection_->addClosedListener(std::move(listener));
}

void StandardReactiveSocket::tryClientResume(
    const ResumeIdentificationToken& token,
    std::shared_ptr<FrameTransport> frameTransport,
    std::unique_ptr<ClientResumeStatusCallback> resumeCallback) {
  // TODO: verify/assert that the new frameTransport is on the same event base
  debugCheckCorrectExecutor();
  checkNotClosed();
  CHECK(frameTransport && !frameTransport->isClosed());

  frameTransport->outputFrameOrEnqueue(
      connection_->createResumeFrame(token).serializeOut());

  connection_->reconnect(std::move(frameTransport), std::move(resumeCallback));
}

bool StandardReactiveSocket::tryResumeServer(
    std::shared_ptr<FrameTransport> frameTransport,
    ResumePosition position) {
  // TODO: verify/assert that the new frameTransport is on the same event base
  debugCheckCorrectExecutor();
  checkNotClosed();
  disconnect();
  // TODO: verify, we should not be receiving any frames, not a single one
  connection_->connect(std::move(frameTransport), /*sendPendingFrames=*/false);
  return connection_->resumeFromPositionOrClose(position);
}

void StandardReactiveSocket::checkNotClosed() const {
  CHECK(connection_) << "ReactiveSocket already closed";
}

DuplexConnection* StandardReactiveSocket::duplexConnection() const {
  debugCheckCorrectExecutor();
  return connection_ ? connection_->duplexConnection() : nullptr;
}

void StandardReactiveSocket::debugCheckCorrectExecutor() const {
  DCHECK(
      !dynamic_cast<folly::EventBase*>(&executor_) ||
      dynamic_cast<folly::EventBase*>(&executor_)->isInEventBaseThread());
}

bool StandardReactiveSocket::isClosed() {
  debugCheckCorrectExecutor();
  return !static_cast<bool>(connection_);
}

} // reactivesocket
<commit_msg>tryClientResume implies the socket is intended to be resumable (#292)<commit_after>// Copyright 2004-present Facebook. All Rights Reserved.

#include "src/StandardReactiveSocket.h"

#include <folly/ExceptionWrapper.h>
#include <folly/Memory.h>
#include <folly/MoveWrapper.h>
#include <folly/io/async/EventBase.h>
#include "src/ClientResumeStatusCallback.h"
#include "src/ConnectionAutomaton.h"
#include "src/FrameTransport.h"
#include "src/RequestHandler.h"
#include "src/automata/ChannelResponder.h"

namespace reactivesocket {

StandardReactiveSocket::~StandardReactiveSocket() {
  debugCheckCorrectExecutor();

  // Force connection closure, this will trigger terminal signals to be
  // delivered to all stream automata.
  close();
}

StandardReactiveSocket::StandardReactiveSocket(
    ReactiveSocketMode mode,
    std::shared_ptr<RequestHandler> handler,
    Stats& stats,
    std::unique_ptr<KeepaliveTimer> keepaliveTimer,
    folly::Executor& executor)
    : handler_(handler),
      connection_(std::make_shared<ConnectionAutomaton>(
          executor,
          [this, handler](
              ConnectionAutomaton& connection,
              StreamId streamId,
              std::unique_ptr<folly::IOBuf> serializedFrame) {
            createResponder(
                handler, connection, streamId, std::move(serializedFrame));
          },
          std::make_shared<StreamState>(stats),
          handler,
          std::bind(
              &StandardReactiveSocket::resumeListener,
              this,
              std::placeholders::_1),
          stats,
          std::move(keepaliveTimer),
          mode)),
      streamsFactory_(connection_, mode),
      executor_(executor) {
  debugCheckCorrectExecutor();
  stats.socketCreated();
}

std::unique_ptr<StandardReactiveSocket>
StandardReactiveSocket::fromClientConnection(
    folly::Executor& executor,
    std::unique_ptr<DuplexConnection> connection,
    std::unique_ptr<RequestHandler> handler,
    ConnectionSetupPayload setupPayload,
    Stats& stats,
    std::unique_ptr<KeepaliveTimer> keepaliveTimer) {
  auto socket = disconnectedClient(
      executor, std::move(handler), stats, std::move(keepaliveTimer));
  socket->clientConnect(
      std::make_shared<FrameTransport>(std::move(connection)),
      std::move(setupPayload));
  return socket;
}

std::unique_ptr<StandardReactiveSocket>
StandardReactiveSocket::disconnectedClient(
    folly::Executor& executor,
    std::unique_ptr<RequestHandler> handler,
    Stats& stats,
    std::unique_ptr<KeepaliveTimer> keepaliveTimer) {
  std::unique_ptr<StandardReactiveSocket> socket(new StandardReactiveSocket(
      ReactiveSocketMode::CLIENT,
      std::move(handler),
      stats,
      std::move(keepaliveTimer),
      executor));
  return socket;
}

std::unique_ptr<StandardReactiveSocket>
StandardReactiveSocket::fromServerConnection(
    folly::Executor& executor,
    std::unique_ptr<DuplexConnection> connection,
    std::unique_ptr<RequestHandler> handler,
    Stats& stats,
    bool isResumable) {
  // TODO: isResumable should come as a flag on Setup frame and it should be
  // exposed to the application code. We should then remove this parameter
  auto socket = disconnectedServer(executor, std::move(handler), stats);
  socket->serverConnect(
      std::make_shared<FrameTransport>(std::move(connection)), isResumable);
  return socket;
}

std::unique_ptr<StandardReactiveSocket>
StandardReactiveSocket::disconnectedServer(
    folly::Executor& executor,
    std::unique_ptr<RequestHandler> handler,
    Stats& stats) {
  std::unique_ptr<StandardReactiveSocket> socket(new StandardReactiveSocket(
      ReactiveSocketMode::SERVER,
      std::move(handler),
      stats,
      nullptr,
      executor));
  return socket;
}

std::shared_ptr<Subscriber<Payload>> StandardReactiveSocket::requestChannel(
    std::shared_ptr<Subscriber<Payload>> responseSink) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  return streamsFactory_.createChannelRequester(
      std::move(responseSink), executor_);
}

void StandardReactiveSocket::requestStream(
    Payload request,
    std::shared_ptr<Subscriber<Payload>> responseSink) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  streamsFactory_.createStreamRequester(
      std::move(request), std::move(responseSink), executor_);
}

void StandardReactiveSocket::requestSubscription(
    Payload request,
    std::shared_ptr<Subscriber<Payload>> responseSink) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  streamsFactory_.createSubscriptionRequester(
      std::move(request), std::move(responseSink), executor_);
}

void StandardReactiveSocket::requestResponse(
    Payload payload,
    std::shared_ptr<Subscriber<Payload>> responseSink) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  streamsFactory_.createRequestResponseRequester(
      std::move(payload), std::move(responseSink), executor_);
}

void StandardReactiveSocket::requestFireAndForget(Payload request) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  Frame_REQUEST_FNF frame(
      streamsFactory_.getNextStreamId(),
      FrameFlags_EMPTY,
      std::move(std::move(request)));
  connection_->outputFrameOrEnqueue(frame.serializeOut());
}

void StandardReactiveSocket::metadataPush(
    std::unique_ptr<folly::IOBuf> metadata) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  connection_->outputFrameOrEnqueue(
      Frame_METADATA_PUSH(std::move(metadata)).serializeOut());
}

void StandardReactiveSocket::createResponder(
    std::shared_ptr<RequestHandler> handler,
    ConnectionAutomaton& connection,
    StreamId streamId,
    std::unique_ptr<folly::IOBuf> serializedFrame) {
  debugCheckCorrectExecutor();

  if (streamId != 0 && !streamsFactory_.registerNewPeerStreamId(streamId)) {
    return;
  }

  auto type = FrameHeader::peekType(*serializedFrame);
  switch (type) {
    case FrameType::SETUP: {
      Frame_SETUP frame;
      if (!connection.deserializeFrameOrError(
              frame, std::move(serializedFrame))) {
        return;
      }
      if (frame.header_.flags_ & FrameFlags_LEASE) {
        // TODO(yschimke) We don't have the correct lease and wait logic above
        // yet
        LOG(WARNING) << "ignoring setup frame with lease";
        //          connectionOutput_.onNext(
        //              Frame_ERROR::badSetupFrame("leases not supported")
        //                  .serializeOut());
        //          disconnect();
      }

      auto streamState = handler->handleSetupPayload(
          *this,
          ConnectionSetupPayload(
              std::move(frame.metadataMimeType_),
              std::move(frame.dataMimeType_),
              std::move(frame.payload_),
              false, // TODO: resumable flag should be received in SETUP frame
              frame.token_));

      // TODO(lehecka): use again
      // connection.useStreamState(streamState);
      break;
    }
    case FrameType::RESUME: {
      Frame_RESUME frame;
      if (!connection.deserializeFrameOrError(
              frame, std::move(serializedFrame))) {
        return;
      }
      auto resumed =
          handler->handleResume(*this, frame.token_, frame.position_);
      if (!resumed) {
        // TODO(lehecka): the "connection" and "this" arguments needs to be
        // cleaned up. It is not intuitive what is their lifetime.
        auto connectionCopy = std::move(connection_);
        connection.closeWithError(
            Frame_ERROR::connectionError("can not resume"));
      }
      break;
    }
    case FrameType::REQUEST_CHANNEL: {
      Frame_REQUEST_CHANNEL frame;
      if (!connection.deserializeFrameOrError(
              frame, std::move(serializedFrame))) {
        return;
      }
      auto automaton = streamsFactory_.createChannelResponder(
          frame.requestN_, streamId, executor_);
      auto requestSink = handler->handleRequestChannel(
          std::move(frame.payload_), streamId, automaton);
      automaton->subscribe(requestSink);
      break;
    }
    case FrameType::REQUEST_STREAM: {
      Frame_REQUEST_STREAM frame;
      if (!connection.deserializeFrameOrError(
              frame, std::move(serializedFrame))) {
        return;
      }
      auto automaton = streamsFactory_.createStreamResponder(
          frame.requestN_, streamId, executor_);
      handler->handleRequestStream(
          std::move(frame.payload_), streamId, automaton);
      break;
    }
    case FrameType::REQUEST_SUB: {
      Frame_REQUEST_SUB frame;
      if (!connection.deserializeFrameOrError(
              frame, std::move(serializedFrame))) {
        return;
      }
      auto automaton = streamsFactory_.createSubscriptionResponder(
          frame.requestN_, streamId, executor_);
      handler->handleRequestSubscription(
          std::move(frame.payload_), streamId, automaton);
      break;
    }
    case FrameType::REQUEST_RESPONSE: {
      Frame_REQUEST_RESPONSE frame;
      if (!connection.deserializeFrameOrError(
              frame, std::move(serializedFrame))) {
        return;
      }
      auto automaton =
          streamsFactory_.createRequestResponseResponder(streamId, executor_);
      handler->handleRequestResponse(
          std::move(frame.payload_), streamId, automaton);
      break;
    }
    case FrameType::REQUEST_FNF: {
      Frame_REQUEST_FNF frame;
      if (!connection.deserializeFrameOrError(
              frame, std::move(serializedFrame))) {
        return;
      }
      // no stream tracking is necessary
      handler->handleFireAndForgetRequest(std::move(frame.payload_), streamId);
      break;
    }
    case FrameType::METADATA_PUSH: {
      Frame_METADATA_PUSH frame;
      if (!connection.deserializeFrameOrError(
              frame, std::move(serializedFrame))) {
        return;
      }
      handler->handleMetadataPush(std::move(frame.metadata_));
      break;
    }
    // Other frames cannot start a stream.

    case FrameType::LEASE:
    case FrameType::KEEPALIVE:
    case FrameType::RESERVED:
    case FrameType::REQUEST_N:
    case FrameType::CANCEL:
    case FrameType::RESPONSE:
    case FrameType::ERROR:
    case FrameType::RESUME_OK:
    default:
      // TODO(lehecka): the "connection" and "this" arguments needs to be
      // cleaned up. It is not intuitive what is their lifetime.
      auto connectionCopy = std::move(connection_);
      connection.closeWithError(Frame_ERROR::unexpectedFrame());
  }
}

std::shared_ptr<StreamState> StandardReactiveSocket::resumeListener(
    const ResumeIdentificationToken& token) {
  debugCheckCorrectExecutor();
  CHECK(false) << "not implemented";
  // TODO(lehecka)
  return nullptr;
  //  return handler_->handleResume(token);
}

void StandardReactiveSocket::clientConnect(
    std::shared_ptr<FrameTransport> frameTransport,
    ConnectionSetupPayload setupPayload) {
  CHECK(frameTransport && !frameTransport->isClosed());
  debugCheckCorrectExecutor();
  checkNotClosed();
  connection_->setResumable(setupPayload.resumable);

  // TODO set correct version
  Frame_SETUP frame(
      setupPayload.resumable ? FrameFlags_RESUME_ENABLE : FrameFlags_EMPTY,
      /*version=*/0,
      connection_->getKeepaliveTime(),
      std::numeric_limits<uint32_t>::max(),
      setupPayload.token,
      std::move(setupPayload.metadataMimeType),
      std::move(setupPayload.dataMimeType),
      std::move(setupPayload.payload));

  // TODO: when the server returns back that it doesn't support resumability, we
  // should retry without resumability

  // making sure we send setup frame first
  frameTransport->outputFrameOrEnqueue(frame.serializeOut());
  // then the rest of the cached frames will be sent
  connection_->connect(std::move(frameTransport), true);
}

void StandardReactiveSocket::serverConnect(
    std::shared_ptr<FrameTransport> frameTransport,
    bool isResumable) {
  debugCheckCorrectExecutor();
  connection_->setResumable(isResumable);
  connection_->connect(std::move(frameTransport), true);
}

void StandardReactiveSocket::close() {
  debugCheckCorrectExecutor();
  if (auto connectionCopy = std::move(connection_)) {
    connectionCopy->close(
        folly::exception_wrapper(), StreamCompletionSignal::SOCKET_CLOSED);
  }
}

void StandardReactiveSocket::disconnect() {
  debugCheckCorrectExecutor();
  checkNotClosed();
  connection_->disconnect(folly::exception_wrapper());
}

std::shared_ptr<FrameTransport> StandardReactiveSocket::detachFrameTransport() {
  debugCheckCorrectExecutor();
  checkNotClosed();
  return connection_->detachFrameTransport();
}

void StandardReactiveSocket::onConnected(std::function<void()> listener) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  connection_->addConnectedListener(std::move(listener));
}

void StandardReactiveSocket::onDisconnected(ErrorCallback listener) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  connection_->addDisconnectedListener(std::move(listener));
}

void StandardReactiveSocket::onClosed(ErrorCallback listener) {
  debugCheckCorrectExecutor();
  checkNotClosed();
  connection_->addClosedListener(std::move(listener));
}

void StandardReactiveSocket::tryClientResume(
    const ResumeIdentificationToken& token,
    std::shared_ptr<FrameTransport> frameTransport,
    std::unique_ptr<ClientResumeStatusCallback> resumeCallback) {
  // TODO: verify/assert that the new frameTransport is on the same event base
  debugCheckCorrectExecutor();
  checkNotClosed();
  CHECK(frameTransport && !frameTransport->isClosed());

  frameTransport->outputFrameOrEnqueue(
      connection_->createResumeFrame(token).serializeOut());

  connection_->setResumable(true);
  connection_->reconnect(std::move(frameTransport), std::move(resumeCallback));
}

bool StandardReactiveSocket::tryResumeServer(
    std::shared_ptr<FrameTransport> frameTransport,
    ResumePosition position) {
  // TODO: verify/assert that the new frameTransport is on the same event base
  debugCheckCorrectExecutor();
  checkNotClosed();
  disconnect();
  // TODO: verify, we should not be receiving any frames, not a single one
  connection_->connect(std::move(frameTransport), /*sendPendingFrames=*/false);
  return connection_->resumeFromPositionOrClose(position);
}

void StandardReactiveSocket::checkNotClosed() const {
  CHECK(connection_) << "ReactiveSocket already closed";
}

DuplexConnection* StandardReactiveSocket::duplexConnection() const {
  debugCheckCorrectExecutor();
  return connection_ ? connection_->duplexConnection() : nullptr;
}

void StandardReactiveSocket::debugCheckCorrectExecutor() const {
  DCHECK(
      !dynamic_cast<folly::EventBase*>(&executor_) ||
      dynamic_cast<folly::EventBase*>(&executor_)->isInEventBaseThread());
}

bool StandardReactiveSocket::isClosed() {
  debugCheckCorrectExecutor();
  return !static_cast<bool>(connection_);
}

} // reactivesocket
<|endoftext|>