message(aMessage),
retries(10), // TODO: make configurable?
timeout(10), // TODO: make configurable?
+ sleeping(false),
writing(false)
{
message.address(address);
}
+void Ipc::UdsSender::swanSong()
+{
+ // did we abort while waiting between retries?
+ if (sleeping)
+ cancelSleep();
+
+ UdsOp::swanSong();
+}
+
void Ipc::UdsSender::start()
{
UdsOp::start();
bool Ipc::UdsSender::doneAll() const
{
- return !writing && UdsOp::doneAll();
+ return !writing && !sleeping && UdsOp::doneAll();
}
void Ipc::UdsSender::write()
debugs(54, 5, HERE << params.conn << " flag " << params.flag << " retries " << retries << " [" << this << ']');
writing = false;
if (params.flag != COMM_OK && retries-- > 0) {
- sleep(1); // do not spend all tries at once; XXX: use an async timed event instead of blocking here; store the time when we started writing so that we do not sleep if not needed?
- write(); // XXX: should we close on error so that conn() reopens?
+ // perhaps a fresh connection and more time will help?
+ conn()->close();
+ sleep();
+ }
+}
+
+/// pause for a while before resending the message
+void Ipc::UdsSender::sleep()
+{
+ Must(!sleeping);
+ sleeping = true;
+ eventAdd("Ipc::UdsSender::DelayedRetry",
+ Ipc::UdsSender::DelayedRetry,
+ new Pointer(this), 1, 0, false); // TODO: Use Fibonacci increments
+}
+
+/// stop sleeping (or do nothing if we were not)
+void Ipc::UdsSender::cancelSleep()
+{
+ if (sleeping) {
+ // Why not delete the event? See Comm::ConnOpener::cancelSleep().
+ sleeping = false;
+ debugs(54, 9, "stops sleeping");
+ }
+}
+
+/// legacy wrapper for Ipc::UdsSender::delayedRetry()
+void Ipc::UdsSender::DelayedRetry(void *data)
+{
+ Pointer *ptr = static_cast<Pointer*>(data);
+ assert(ptr);
+ if (UdsSender *us = dynamic_cast<UdsSender*>(ptr->valid())) {
+ // get back inside AsyncJob protection by scheduling an async job call
+ typedef NullaryMemFunT<Ipc::UdsSender> Dialer;
+ AsyncCall::Pointer call = JobCallback(54, 4, Dialer, us, Ipc::UdsSender::delayedRetry);
+ ScheduleCallHere(call);
+ }
+ delete ptr;
+}
+
+/// make another sending attempt after a pause
+void Ipc::UdsSender::delayedRetry()
+{
+ debugs(54, 5, HERE << sleeping);
+ if (sleeping) {
+ sleeping = false;
+ write(); // reopens the connection if needed
}
}
UdsSender(const String& pathAddr, const TypedMsgHdr& aMessage);
protected:
+ virtual void swanSong(); // UdsOp (AsyncJob) API
virtual void start(); // UdsOp (AsyncJob) API
virtual bool doneAll() const; // UdsOp (AsyncJob) API
virtual void timedout(); // UdsOp API
private:
+ void sleep();
+ void cancelSleep();
+ static void DelayedRetry(void *data);
+ void delayedRetry();
+
void write(); ///< schedule writing
void wrote(const CommIoCbParams& params); ///< done writing or error
TypedMsgHdr message; ///< what to send
int retries; ///< how many times to try after a write error
int timeout; ///< total time to send the message
+ bool sleeping; ///< whether we are waiting to retry a failed write
bool writing; ///< whether Comm started and did not finish writing
private: