Hello,
We're working on an application that can download files. We began seeing a problem with Casablanca 1.0 (also in 1.1). We originally developed against 0.6.21. It crashes in streams.h here:
```
// Always have to release if acquire returned true.
if(acquired)
{
buffer.release(data, 0);
}
```
In the failing case, data is nullptr. This code is new in 1.0, presumably to fix a memory leak.
Curiously, the assignment a few lines earlier:
const bool acquired = buffer.acquire(data, available);
is returning true and data is set to nullptr. I got a little lost stepping through all the template code although it does seem that in in producerconsumerstream.h, you can return both true and nullptr:
```
virtual bool acquire(_Out_writes_ (count) _CharType*& ptr, _In_ size_t& count)
{
pplx::extensibility::scoped_critical_section_t l(m_lock);
if (!this->can_read()) return false;
if (m_blocks.empty())
{
count = 0;
ptr = nullptr;
// If the write head has been closed then have reached the end of the
// stream (return true), otherwise more data could be written later (return false).
return !this->can_write();
}
else
{
auto block = m_blocks.front();
count = block->rd_chars_left();
ptr = block->rbegin();
_ASSERTE(ptr != nullptr);
return true;
}
}
```
I added a test for nullptr to the acquire test prior to the buffer.release. This seems to have eliminated the crash, although it was intermittent so I'm not yet certain. The change is safe, although I'm curious if it is merely masking another issue (either in our app or elsewhere in Casablanca).
Here is the loop in our application that fails.
```
concurrency::streams::basic_ostream<unsigned char> file_stream;
try
{
file_stream = concurrency::streams::file_stream<unsigned char>::open_ostream(localFilePath).get();
}
catch (const std::exception& ex)
{
LOG_ERROR("Cannot open " << localFilePath << " exception: " << ex.what());
return HPConnectedAuthorization::ErrorCodes::UNSUCCESSFUL;
}
try
{
//Get the Response
web::http::client::http_client client(serviceUrl, client_config);
web::http::http_response response = client.request(request).get();
web::http::http_headers headers = response.headers();
web::http::status_code status_code = response.status_code();
if(web::http::status_codes::OK == status_code)
{
//Parse JSON
concurrency::streams::basic_istream<unsigned char> body = response.body();
try
{
while(body.is_eof() == false)
{
body.read(file_stream.streambuf(), DOWNLOAD_BUFFER_SIZE).get();
if(callback != nullptr)
{
if(callback(body.tell()) == false)
{
LOG_WARN("Download cancelled by callback method");
body.close();
file_stream.close();
FileUtil::deleteFile(localFilePath);
return HPConnectedAuthorization::ErrorCodes::UNSUCCESSFUL;
}
}
}
if(callback != nullptr)
{
callback(body.tell());
}
body.close();
file_stream.close();
.
.
.
```
We're working on an application that can download files. We began seeing a problem with Casablanca 1.0 (also in 1.1). We originally developed against 0.6.21. It crashes in streams.h here:
```
// Always have to release if acquire returned true.
if(acquired)
{
buffer.release(data, 0);
}
```
In the failing case, data is nullptr. This code is new in 1.0, presumably to fix a memory leak.
Curiously, the assignment a few lines earlier:
const bool acquired = buffer.acquire(data, available);
is returning true and data is set to nullptr. I got a little lost stepping through all the template code although it does seem that in in producerconsumerstream.h, you can return both true and nullptr:
```
virtual bool acquire(_Out_writes_ (count) _CharType*& ptr, _In_ size_t& count)
{
pplx::extensibility::scoped_critical_section_t l(m_lock);
if (!this->can_read()) return false;
if (m_blocks.empty())
{
count = 0;
ptr = nullptr;
// If the write head has been closed then have reached the end of the
// stream (return true), otherwise more data could be written later (return false).
return !this->can_write();
}
else
{
auto block = m_blocks.front();
count = block->rd_chars_left();
ptr = block->rbegin();
_ASSERTE(ptr != nullptr);
return true;
}
}
```
I added a test for nullptr to the acquire test prior to the buffer.release. This seems to have eliminated the crash, although it was intermittent so I'm not yet certain. The change is safe, although I'm curious if it is merely masking another issue (either in our app or elsewhere in Casablanca).
Here is the loop in our application that fails.
```
concurrency::streams::basic_ostream<unsigned char> file_stream;
try
{
file_stream = concurrency::streams::file_stream<unsigned char>::open_ostream(localFilePath).get();
}
catch (const std::exception& ex)
{
LOG_ERROR("Cannot open " << localFilePath << " exception: " << ex.what());
return HPConnectedAuthorization::ErrorCodes::UNSUCCESSFUL;
}
try
{
//Get the Response
web::http::client::http_client client(serviceUrl, client_config);
web::http::http_response response = client.request(request).get();
web::http::http_headers headers = response.headers();
web::http::status_code status_code = response.status_code();
if(web::http::status_codes::OK == status_code)
{
//Parse JSON
concurrency::streams::basic_istream<unsigned char> body = response.body();
try
{
while(body.is_eof() == false)
{
body.read(file_stream.streambuf(), DOWNLOAD_BUFFER_SIZE).get();
if(callback != nullptr)
{
if(callback(body.tell()) == false)
{
LOG_WARN("Download cancelled by callback method");
body.close();
file_stream.close();
FileUtil::deleteFile(localFilePath);
return HPConnectedAuthorization::ErrorCodes::UNSUCCESSFUL;
}
}
}
if(callback != nullptr)
{
callback(body.tell());
}
body.close();
file_stream.close();
.
.
.
```