Re: Automatic refresh for local Maildir's mail counts -- an attempt based on named pipes
Linux-Fan <Ma_Sys.ma-S0/[email protected]> Tue, 27 Mar 2018 21:32:42 +0200
| Newsgroups | gmane.mail.cone |
|---|---|
| Message-ID | <cone.1522179162.702706.17983.1000@masysma-3> |
This is a MIME GnuPG-signed message. If you see this text, it means that
your E-mail or Usenet software does not support MIME signed messages.
The Internet standard for MIME PGP messages, RFC 2015, was published in 1996.
To open this message correctly you will need to install E-mail or Usenet
software that supports modern Internet standards.
--===============9199632980422032743==
Content-Type: multipart/signed;
boundary="=_masysma-3-17983-1522179162-0002";
micalg=pgp-sha1; protocol="application/pgp-signature"
This is a MIME GnuPG-signed message. If you see this text, it means that
your E-mail or Usenet software does not support MIME signed messages.
The Internet standard for MIME PGP messages, RFC 2015, was published in 1996.
To open this message correctly you will need to install E-mail or Usenet
software that supports modern Internet standards.
--=_masysma-3-17983-1522179162-0002
Content-Type: multipart/mixed; boundary="=_masysma-3-17983-1522179162-0001-2"
This is a MIME-formatted message. If you see this text it means that your
E-mail software does not support MIME-formatted messages.
--=_masysma-3-17983-1522179162-0001-2
Content-Type: text/plain; format=flowed; delsp=yes; charset=utf-8
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
X-Mime-Autoconverted: from 8bit to quoted-printable by mimegpg
Sam Varshavchik writes:
> Linux-Fan writes:
>
>> The actual error (why I initially got endless loops when trying to do =
this), =20
>> was with my understanding of named pipes: What actually caused the end=
less =20
>> loop was that whenever I notified Cone from offlineimap, the named pip=
e got =20
>> closed in the process, causing POLLHUP to be returned (immediately) on =
all =20
>> subsequent poll invocations.
>
> Closing and reopening is fine, but the usual approach here is to open t=
he =20
> named pipe twice, both for reading and writing, and having the named pi=
pe =20
> opened for writing will prevent this POLLHUP. But, again, this is fine. =
The =20
> only problem this might potentially cause is that there's a brief perio=
d of =20
> time when the named pipe is closed, and anything that happens to open i=
t for =20
> writing, at that time, will fail unless it's specifically coded to hand=
le =20
> this eventuality.
Thank you very much for telling me the "usual approach" :) which I think =20
yields a better software design because the complexity of processing the =20
poll results is reduced.
Although I did not like it that much design-wise, from what I understand =20
even with the close+open implementation there is no race condition becaus=
e =20
the pipe will just block on the writing end until Cone opens it again for =20
reading.
> I took a closer look at this code.
>
> The named pipe should have the FD_CLOEXEC flag set, to prevent it from =
being =20
> inherited by child processes.
OK. Thanks for pointing this out.
> You also place the file descriptor into the poll vector even if it's no=
t =20
> used, if the file descriptor is -1. I was actually surprised to learn t=
hat =20
> this is not an error, and this is actually documented as a no-op, in th=
e =20
> poll() manually page. You got lucky there=E2=80=A6
I actually read that in the documentation when I learned about pipes (I =20
never used open() explicitly before) :) and made use of this behaviour =20
achieve less complex conditions for adding/removing the file descriptor =20
to/from the vector. I am still not sure if it is the better design this w=
ay =20
compared to just having two if-statements guard the add and remove parts? =
As =20
a little improvement, I have added a comment to explain that this behavio=
ur =20
is explicitly used.
> If you'd like to tweak this further and set FD_CLOEXEC on the pipe, you =
can =20
> go ahead and do this, but this looks ok, as is.
Attached `patch_cone_refresh_with_named_pipe_3.diff` is an (IMHO) improve=
d =20
patch which
* sets CLOEXEC
* opens two file descriptors and does not re-open the file from inside
the event loop
* explains the poll behaviour for file descriptors < 0 in a comment
as to cause less confusion
Is there anything else that can be done before the feature can become par=
t =20
of a Cone release?
Yours Faithfully
Linux-Fan
--=_masysma-3-17983-1522179162-0001-2
Content-Type: text/x-diff; charset=utf-8
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
FILENAME="patch_cone_refresh_with_named_pipe_3.diff"
diff -Nau -x Makefile.in -x Makefile -x .deps -x '*.cache' -x buildversion.H -x '*.log' -x config.status -r cone-beforechg/cone/cone.C cone-0.96.2.20180216/cone/cone.C
--- cone-beforechg/cone/cone.C 2018-03-25 16:10:45.317386165 +0200
+++ cone-0.96.2.20180216/cone/cone.C 2018-03-26 23:12:53.182300974 +0200
@@ -429,6 +429,7 @@
myServer **selectedServer)
{
CursesHierarchy hierarchy_screen( &myServer::hierarchy, mainScreen);
+ myServer::setCursesHierarchyPointerForRefreshing(&hierarchy_screen);
titleBar->setTitles(_("FOLDERS"), "");
@@ -474,6 +475,8 @@
*selectedFolder=hierarchy_screen.folderSelected;
if (selectedServer)
*selectedServer=hierarchy_screen.serverSelected;
+
+ myServer::setCursesHierarchyPointerForRefreshing(NULL);
}
void hierarchyScreen(void *dummy)
@@ -1025,6 +1028,8 @@
delete myServer::remoteConfigAccount;
myServer::remoteConfigAccount=NULL;
}
+
+ myServer::closePollForRefreshMessageCount();
}
//
@@ -1142,7 +1147,7 @@
int recover=0;
Macros macroBuffer;
- while ((optc=getopt(argc, argv, "vrCc:")) != -1)
+ while ((optc=getopt(argc, argv, "vrCc:m:")) != -1)
{
switch (optc) {
case 'v':
@@ -1151,6 +1156,16 @@
case 'r':
recover=1;
break;
+ case 'm':
+ // try to delete if existent, ignore fail
+ unlink(optarg);
+ if(mkfifo(optarg, 0644) < 0)
+ {
+ perror("Failed to create FIFO");
+ exit(1);
+ }
+ myServer::setPollForRefreshMessageCount(optarg);
+ break;
case 'c':
myServer::configDir=optarg;
diff -Nau -x Makefile.in -x Makefile -x .deps -x '*.cache' -x buildversion.H -x '*.log' -x config.status -r cone-beforechg/cone/curseshierarchy.C cone-0.96.2.20180216/cone/curseshierarchy.C
--- cone-beforechg/cone/curseshierarchy.C 2018-03-25 16:10:45.301386438 +0200
+++ cone-0.96.2.20180216/cone/curseshierarchy.C 2018-03-25 17:08:13.627911893 +0200
@@ -985,6 +985,26 @@
me->processDeletedFolder(parent, deleted, updatedFolder);
}
+bool CursesHierarchy::RefreshIterator::visit(Hierarchy::Folder* f)
+{
+ if(f != NULL && this->cs != NULL)
+ f->updateInfo(this->cs, true);
+ return true;
+}
+
+bool CursesHierarchy::RefreshIterator::visit(Hierarchy::Server* s)
+{
+ if(s != NULL)
+ this->cs = s->getServer();
+ return true;
+}
+
+void CursesHierarchy::refreshAllFolders()
+{
+ RefreshIterator iterator;
+ getHierarchy()->root.prefixIterate(iterator);
+}
+
///////////
bool CursesHierarchy::processKey(const Curses::Key &key)
{
diff -Nau -x Makefile.in -x Makefile -x .deps -x '*.cache' -x buildversion.H -x '*.log' -x config.status -r cone-beforechg/cone/curseshierarchy.H cone-0.96.2.20180216/cone/curseshierarchy.H
--- cone-beforechg/cone/curseshierarchy.H 2018-03-25 16:10:45.321386096 +0200
+++ cone-0.96.2.20180216/cone/curseshierarchy.H 2018-03-25 17:10:14.744631348 +0200
@@ -61,6 +61,14 @@
};
+ class RefreshIterator: public Hierarchy::EntryIterator {
+ private:
+ myServer* cs = NULL;
+ public:
+ bool visit(Hierarchy::Folder* f);
+ bool visit(Hierarchy::Server* s);
+ };
+
public:
friend class DrawIterator;
@@ -84,6 +92,7 @@
bool drawErase(Hierarchy::Server *server, bool doErase);
void visible(Hierarchy::Entry *e);
+ void refreshAllFolders();
private:
bool drawErase(std::string str1, int col1,
diff -Nau -x Makefile.in -x Makefile -x .deps -x '*.cache' -x buildversion.H -x '*.log' -x config.status -r cone-beforechg/cone/myserver.C cone-0.96.2.20180216/cone/myserver.C
--- cone-beforechg/cone/myserver.C 2018-03-25 16:10:45.313386233 +0200
+++ cone-0.96.2.20180216/cone/myserver.C 2018-03-27 21:00:03.401109898 +0200
@@ -23,6 +23,7 @@
#include <fcntl.h>
#include <pwd.h>
#include <unistd.h>
+#include <poll.h>
#include <iostream>
@@ -42,6 +43,10 @@
extern void folderIndexScreen(void *);
extern void hierarchyScreen(void *);
+int myServer::pollFDForRefreshMessageCount = -1;
+int myServer::pollFDForRefreshMessageCountWriteDirection = -1;
+CursesHierarchy* myServer::cursesHierarchyForRefreshing = NULL;
+
myServer::myServer(string name, string urlArg)
: serverName(name), url(urlArg), mailCheckInterval(300), server(NULL),
currentFolder(NULL), hierarchyEntry(NULL)
@@ -273,6 +278,16 @@
if (alarmCalled)
continue; // Something might've happened...
+ // poll() ignores file descriptors less than 0, thus the
+ // `pollFDForRefresh` can always be added regardless of the
+ // feature being used or not.
+ struct pollfd pollFDForRefresh = {
+ myServer::pollFDForRefreshMessageCount,
+ POLLIN,
+ 0
+ };
+ fds.push_back(pollFDForRefresh);
+
if (mail::account::poll(fds, ioTimeout) < 0)
{
if (errno != EINTR)
@@ -281,6 +296,17 @@
break;
}
}
+
+ char buf[128];
+ int pollev = fds.back().revents;
+ fds.pop_back();
+ if((pollev & POLLIN) && read(
+ myServer::pollFDForRefreshMessageCount,
+ buf, sizeof(buf)) > 0 &&
+ myServer::pollFDForRefreshMessageCount != -1 &&
+ myServer::cursesHierarchyForRefreshing != NULL)
+ myServer::cursesHierarchyForRefreshing->
+ refreshAllFolders();
}
statusBar->notbusy();
@@ -1032,3 +1058,39 @@
finishCheckingNewMail();
}
+
+void myServer::setPollForRefreshMessageCount(string fn)
+{
+ if((myServer::pollFDForRefreshMessageCount =
+ open(fn.c_str(), O_RDONLY | O_CLOEXEC | O_NONBLOCK)) < 0)
+ {
+ perror("Failed to open FIFO for reading");
+ exit(1);
+ }
+ // Open another file descriptor for writing such that the pipe is
+ // kept open regardless of any external application closing it.
+ if((myServer::pollFDForRefreshMessageCountWriteDirection =
+ open(fn.c_str(), O_WRONLY | O_CLOEXEC)) < 0)
+ {
+ perror("Failed to open FIFO for writing");
+ exit(1);
+ }
+}
+
+void myServer::closePollForRefreshMessageCount()
+{
+ if(myServer::pollFDForRefreshMessageCount != -1)
+ {
+ // POSIX logic: close returns 0 on success thus true on fail...
+ if(close(myServer::pollFDForRefreshMessageCount))
+ perror("Failed to close message count update file "
+ "descriptor");
+ close(myServer::pollFDForRefreshMessageCountWriteDirection);
+ myServer::pollFDForRefreshMessageCount = -1;
+ }
+}
+
+void myServer::setCursesHierarchyPointerForRefreshing(CursesHierarchy* h)
+{
+ myServer::cursesHierarchyForRefreshing = h;
+}
diff -Nau -x Makefile.in -x Makefile -x .deps -x '*.cache' -x buildversion.H -x '*.log' -x config.status -r cone-beforechg/cone/myserver.H cone-0.96.2.20180216/cone/myserver.H
--- cone-beforechg/cone/myserver.H 2018-03-25 16:10:45.313386233 +0200
+++ cone-0.96.2.20180216/cone/myserver.H 2018-03-27 21:00:26.741440002 +0200
@@ -14,6 +14,7 @@
#include "curses/timer.H"
#include "myreadfolders.H"
#include "certificates.H"
+#include "curseshierarchy.H"
#include <string>
#include <vector>
@@ -86,6 +87,13 @@
std::map<std::string, std::map<std::string, std::string>
> folder_configuration;
+ // Allow refreshing message counts through messages on a named pipe.
+ // Necessary file descriptor for reading, the other direction to keep
+ // the pipe open and GUI pointer.
+ static int pollFDForRefreshMessageCount;
+ static int pollFDForRefreshMessageCountWriteDirection;
+ static CursesHierarchy* cursesHierarchyForRefreshing;
+
public:
bool updateServerConfiguration(std::string name, std::string value);
std::string getServerConfiguration(std::string name);
@@ -220,6 +228,10 @@
void addHierarchy(bool assignRows);
void showHierarchy();
void updateHierarchy();
+
+ static void setPollForRefreshMessageCount(std::string fn);
+ static void closePollForRefreshMessageCount();
+ static void setCursesHierarchyPointerForRefreshing(CursesHierarchy* h);
private:
void addTopLevelFolders();
void addTopLevelFolder(std::string);
--=_masysma-3-17983-1522179162-0001-2--
--=_masysma-3-17983-1522179162-0002
Content-Type: application/pgp-signature
Content-Transfer-Encoding: 7bit
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEEkKMBOo1F/f2B6jfK+Elw7bKeCLUFAlq6nFoACgkQ+Elw7bKe
CLXxLg//TYPC+JLoRGy2F2A+/r1mk2Hi9Xs3mmAWokDvztgkoq/KVzheewhjVXGx
mJyRDbhwJkb4aneUHM2JJxvyO7wV5TaJglN+ty2eCRlkyGFw24K2TrEhvjqvroie
IccHMSk96zOGz8fJjDNfu5930FHrVYo/GIv8lZlcAMe4OPoUms2xIDlO2z6wKv4Y
P0sbp3WS8OBxpWKLPfEZVy9Pj+p5gzZQOBHAcKoT5A+4bx1BIQ3elqtlyVLXU6fE
lN7UV1sqyL7Wi7ZHXQY7mti5qHuisnxl2PDH+hoWJwrk5QaVOIiHPofojNuJWK+2
zBoREsIciH/3pVLyXg2nJNefu0gVbj65pvCFo+fzohYIUhau/btcwxHE6/p1ZPkK
e3w1svW0oc5oQCUVZFTXUkplQ1guC8y62BGUe8hiQYZRNGKmhsVgDgY32OXWD4oN
1D/b2crHUMp0/4tys9AcoA6HtvzR1zLZqjbDL5iXzW+3LN5pWrjgVcCsaZcqnBhP
IzSh9O/g1PRUuWE+0xF0HuVT6DlmRlGDJ1QdkXQI794GicNSBEoGBNYqBSB9eVwB
1sjuBvWep+H06LcXCuqaxV1gvZOiTgC8oziNGwNYk6eGny8gZcmEsnHjnUo7QrLd
64LsBEwVnsy/qN0ELJ9G6G3NzeW5nlhAmMhUJH8aHiz/EOIqS+c=
=wEUO
-----END PGP SIGNATURE-----
--=_masysma-3-17983-1522179162-0002--
--===============9199632980422032743==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
--===============9199632980422032743==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
_______________________________________________
Courier-cone mailing list
[email protected]
Unsubscribe: https://lists.sourceforge.net/lists/listinfo/courier-cone
--===============9199632980422032743==--