Back to index

nux  3.0.0
AsyncFileWriter.cpp
Go to the documentation of this file.
00001 // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
00002 /*
00003  * Copyright 2011 Inalogic® Inc.
00004  *
00005  * This program is free software: you can redistribute it and/or modify it
00006  * under the terms of the GNU Lesser General Public License, as
00007  * published by the  Free Software Foundation; either version 2.1 or 3.0
00008  * of the License.
00009  *
00010  * This program is distributed in the hope that it will be useful, but
00011  * WITHOUT ANY WARRANTY; without even the implied warranties of
00012  * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
00013  * PURPOSE.  See the applicable version of the GNU Lesser General Public
00014  * License for more details.
00015  *
00016  * You should have received a copy of both the GNU Lesser General Public
00017  * License along with this program. If not, see <http://www.gnu.org/licenses/>
00018  *
00019  * Authored by: Tim Penhey <tim.penhey@canonical.com>
00020  *
00021  */
00022 
00023 #include "NuxCore.h"
00024 #include "AsyncFileWriter.h"
00025 
00026 #include <sstream>
00027 
00028 #include <gio/gio.h>
00029 
00030 #include <iostream>
00031 
00032 namespace nux
00033 {
00034 
00039 class AsyncFileWriter::Impl
00040 {
00041 public:
00042   Impl(AsyncFileWriter* owner, std::string const& filename);
00043   ~Impl();
00044 
00045   void Write(std::string const& data);
00046   void Close();
00047 
00048   void ProcessAsync();
00049 
00050   static void AppendAsyncCallback(GFile* source, GAsyncResult* res, Impl* impl);
00051   static void WriteAsyncCallback(GOutputStream* source, GAsyncResult* res, Impl* impl);
00052   static void CloseAsyncCallback(GOutputStream* source, GAsyncResult* res, Impl* impl);
00053 
00054   AsyncFileWriter* owner_;
00055   GCancellable* cancel_;
00056   GFile* file_;
00057   GFileOutputStream* output_stream_;
00058   bool close_pending_;
00059   bool pending_async_call_;
00060 
00061   std::stringstream pending_content_;
00062   std::string data_to_write_;
00063 };
00064 
00065 
00066 AsyncFileWriter::Impl::Impl(AsyncFileWriter* owner, std::string const& filename)
00067   : owner_(owner)
00068   , cancel_(g_cancellable_new())
00069   , file_(g_file_new_for_path(filename.c_str()))
00070   , output_stream_(0)
00071   , close_pending_(false)
00072   , pending_async_call_(true)
00073 {
00074   g_file_append_to_async(file_,
00075                          G_FILE_CREATE_NONE,
00076                          G_PRIORITY_DEFAULT,
00077                          cancel_,
00078                          (GAsyncReadyCallback)&AsyncFileWriter::Impl::AppendAsyncCallback,
00079                          this);
00080 }
00081 
00082 AsyncFileWriter::Impl::~Impl()
00083 {
00084   if (pending_async_call_)
00085   {
00086     g_cancellable_cancel(cancel_);
00087   }
00088   // make sure the file is closed.
00089   if (output_stream_)
00090   {
00091     // If we had an output stream, sync write any pending content.
00092     if (pending_content_.tellp())
00093     {
00094       std::string data(pending_content_.str());
00095       gsize bytes_written;
00096       g_output_stream_write_all((GOutputStream*)output_stream_,
00097                                 data.c_str(),
00098                                 data.size(),
00099                                 &bytes_written,
00100                                 NULL, NULL);
00101     }
00102     owner_->closed.emit();
00103     g_object_unref(output_stream_);
00104   }
00105 
00106   g_object_unref(file_);
00107   g_object_unref(cancel_);
00108 }
00109 
00110 void AsyncFileWriter::Impl::AppendAsyncCallback(GFile* source,
00111                                                 GAsyncResult* res,
00112                                                 Impl* impl)
00113 {
00114   GError* error = NULL;
00115   GFileOutputStream* stream = g_file_append_to_finish(source, res, &error);
00116   if (error) {
00117     // Cancelled callbacks call back, but have a cancelled error code.
00118     if (error->code != G_IO_ERROR_CANCELLED) {
00119       std::cerr << error->message << "\n";
00120     }
00121     g_error_free(error);
00122     return;
00123   }
00124   impl->output_stream_ = stream;
00125   impl->pending_async_call_ = false;
00126   impl->owner_->opened.emit();
00127   impl->ProcessAsync();
00128 }
00129 
00130 void AsyncFileWriter::Impl::Write(std::string const& data)
00131 {
00132   if (close_pending_) return;
00133   // TODO: lock the pending_content_ access
00134   pending_content_ << data;
00135   ProcessAsync();
00136 }
00137 
00138 void AsyncFileWriter::Impl::ProcessAsync()
00139 {
00140   if (output_stream_ == NULL || pending_async_call_) return;
00141 
00142   if (pending_content_.tellp())
00143   {
00144     // TODO: lock the pending_content_ access
00145     data_to_write_ = pending_content_.str();
00146     g_output_stream_write_async((GOutputStream*)output_stream_,
00147                                 data_to_write_.c_str(),
00148                                 data_to_write_.size(),
00149                                 G_PRIORITY_DEFAULT,
00150                                 cancel_,
00151                                 (GAsyncReadyCallback)&AsyncFileWriter::Impl::WriteAsyncCallback,
00152                                 this);
00153     pending_async_call_ = true;
00154   }
00155   else if (close_pending_)
00156   {
00157     g_output_stream_close_async((GOutputStream*)output_stream_,
00158                                 G_PRIORITY_DEFAULT,
00159                                 cancel_,
00160                                 (GAsyncReadyCallback)&AsyncFileWriter::Impl::CloseAsyncCallback,
00161                                 this);
00162     pending_async_call_ = true;
00163   }
00164 }
00165 
00166 void AsyncFileWriter::Impl::WriteAsyncCallback(GOutputStream* source,
00167                                                GAsyncResult* res,
00168                                                Impl* impl)
00169 {
00170   GError* error = NULL;
00171   gssize g_bytes_written = g_output_stream_write_finish(source, res, &error);
00172   if (error) {
00173     // Cancelled callbacks call back, but have a cancelled error code.
00174     if (error->code != G_IO_ERROR_CANCELLED) {
00175       std::cerr << error->message << "\n";
00176     }
00177     g_error_free(error);
00178     return;
00179   }
00180   // g_bytes_written is signed from gio, but only negative if there is an error.
00181   // The error should be set too if there was an error, so no negative bytes
00182   // written get past here.
00183   std::size_t bytes_written = g_bytes_written;
00184   impl->pending_async_call_ = false;
00185   // TODO: lock the pending_content_ access
00186   std::string data = impl->pending_content_.str();
00187   if (bytes_written >= data.size()) {
00188     // There should be no reason why bytes_written should be greater than the
00189     // number of bytes in the stream, but this is paranoia.
00190     impl->pending_content_.str("");
00191   } else {
00192     impl->pending_content_.str(data.substr(bytes_written));
00193   }
00194   impl->ProcessAsync();
00195 }
00196 
00197 void AsyncFileWriter::Impl::Close()
00198 {
00199   close_pending_ = true;
00200   ProcessAsync();
00201 }
00202 
00203 void AsyncFileWriter::Impl::CloseAsyncCallback(GOutputStream* source,
00204                                                GAsyncResult* res,
00205                                                Impl* impl)
00206 {
00207   GError* error = NULL;
00208   g_output_stream_close_finish(source, res, &error);
00209   if (error) {
00210     // Cancelled callbacks call back, but have a cancelled error code.
00211     if (error->code != G_IO_ERROR_CANCELLED) {
00212       std::cerr << error->message << "\n";
00213     }
00214     g_error_free(error);
00215     return;
00216   }
00217   g_object_unref(impl->output_stream_);
00218   impl->output_stream_ = 0;
00219   impl->owner_->closed.emit();
00220 }
00221 
00222 AsyncFileWriter::AsyncFileWriter(std::string const& filename)
00223   : pimpl(new Impl(this, filename))
00224 {}
00225 
00226 AsyncFileWriter::~AsyncFileWriter()
00227 {
00228   delete pimpl;
00229 }
00230 
00231 void AsyncFileWriter::Write(std::string const& data)
00232 {
00233   pimpl->Write(data);
00234 }
00235 
00236 void AsyncFileWriter::Close()
00237 {
00238   pimpl->Close();
00239 }
00240 
00241 bool AsyncFileWriter::IsClosing() const
00242 {
00243   return pimpl->close_pending_;
00244 }
00245 
00246 
00247 } // namespace nux