Support for reading and writing 0 bytes to fdstore
[archive/platform/core/system/libConfig.git] / src / config / fdstore.cpp
1 /*
2  *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Jan Olszak <j.olszak@samsung.com>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License
17  */
18
19 /**
20  * @file
21  * @author Jan Olszak (j.olszak@samsung.com)
22  * @brief  Definition of a class for writing and reading data from a file descriptor
23  */
24
25 #include "config.hpp"
26
27 #include "config/fdstore.hpp"
28 #include "config/exception.hpp"
29
30 #include <cstring>
31 #include <cerrno>
32 #include <unistd.h>
33 #include <chrono>
34 #include <poll.h>
35
36 namespace config {
37
38 namespace {
39
40 void waitForEvent(int fd,
41                   short event,
42                   const std::chrono::high_resolution_clock::time_point deadline)
43 {
44     // Wait for the rest of the data
45     struct pollfd fds[1];
46     fds[0].fd = fd;
47     fds[0].events = event | POLLHUP;
48
49     for (;;) {
50         std::chrono::milliseconds timeoutMS =
51             std::chrono::duration_cast<std::chrono::milliseconds>(deadline - std::chrono::high_resolution_clock::now());
52         if (timeoutMS.count() < 0) {
53             throw ConfigException("Timeout");
54         }
55
56         int ret = ::poll(fds, 1 /*fds size*/, timeoutMS.count());
57
58         if (ret == -1) {
59             if (errno == EINTR) {
60                 continue;
61             }
62             throw ConfigException("Error in poll: " + std::string(strerror(errno)));
63         }
64
65         if (ret == 0) {
66             throw ConfigException("Timeout");
67         }
68
69         if (fds[0].revents & POLLHUP) {
70             throw ConfigException("Peer disconnected");
71         }
72
73         // Here Comes the Sun
74         break;
75     }
76 }
77
78 } // namespace
79
80 FDStore::FDStore(int fd)
81     : mFD(fd)
82 {
83 }
84
85 FDStore::FDStore(const FDStore& store)
86     : mFD(store.mFD)
87 {
88 }
89
90 FDStore::~FDStore()
91 {
92 }
93
94 void FDStore::write(const void* bufferPtr, const size_t size, const unsigned int timeoutMS)
95 {
96     std::chrono::high_resolution_clock::time_point deadline = std::chrono::high_resolution_clock::now() +
97                                                               std::chrono::milliseconds(timeoutMS);
98
99     size_t nTotal = 0;
100     for (;;) {
101         int n  = ::write(mFD,
102                          reinterpret_cast<const char*>(bufferPtr) + nTotal,
103                          size - nTotal);
104         if (n >= 0) {
105             nTotal += n;
106             if (nTotal == size) {
107                 // All data is written, break loop
108                 break;
109             }
110         } else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
111             // Neglected errors
112         } else {
113             throw ConfigException("Error during writing: " + std::string(strerror(errno)));
114         }
115
116         waitForEvent(mFD, POLLOUT, deadline);
117     }
118 }
119
120 void FDStore::read(void* bufferPtr, const size_t size, const unsigned int timeoutMS)
121 {
122     std::chrono::high_resolution_clock::time_point deadline = std::chrono::high_resolution_clock::now() +
123                                                               std::chrono::milliseconds(timeoutMS);
124
125     size_t nTotal = 0;
126     for (;;) {
127         int n  = ::read(mFD,
128                         reinterpret_cast<char*>(bufferPtr) + nTotal,
129                         size - nTotal);
130         if (n >= 0) {
131             nTotal += n;
132             if (nTotal == size) {
133                 // All data is read, break loop
134                 break;
135             }
136             if (n == 0) {
137                 throw ConfigException("Peer disconnected");
138             }
139         } else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
140             // Neglected errors
141         } else {
142             throw ConfigException("Error during reading: " + std::string(strerror(errno)));
143         }
144
145         waitForEvent(mFD, POLLIN, deadline);
146     }
147 }
148 } // namespace config