nux-1.14.0
|
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