Handling of ECONNRESET socket error in asyncore.py
It just thought about the fact that zope on linux seemingly does ignore when the user presses of the stop on his browser. I.e., if you access a e.g. long running python(script) and press "Stop" on the browser and you'll see the thread which runs the script is still working. Extremely ugly IMO. Well, it also happens on win. I just looked at asyncore.dispatcher, and there's the recv() method: def recv (self, buffer_size): try: ... except socket.error, why: # winsock sometimes throws ENOTCONN if why[0] in [ECONNRESET, ENOTCONN, ESHUTDOWN]: self.handle_close() return '' else: raise socket.error, why The browser closes the connection when the Stop button is pressed, that means that the recieving end gets a RST, and that seems to throw a socket.error, with why[0] == ECONNRESET. Well, I know jack about the python socket class, but a strategical placed "print" tells more than a thousand words ;-). handle_close() doesn't also do much, just logs and closes the connection. So, the poor thread never does never find out about the fact that all his work is for nothing, and happily continues. Can't this be fixed anyhow? cheers, oliver
AFAIK, there is no way to "kill" a thread. On top of that, there is no way to know if a browser "stop" actually means "stop processing" (for example in the case of a long-running method, a browser stop might just mean "I went to another web page, I'll be back, continue running"). This is handled in ASP by requiring the programmer to insert stuff like: if REQUEST.isConnectionClosed: return else: continue processing. This might be a reasonable thing to add. - C ----- Original Message ----- From: "Oliver Bleutgen" <myzope@gmx.net> To: <zope-dev@zope.org> Sent: Friday, August 23, 2002 5:06 PM Subject: [Zope-dev] Handling of ECONNRESET socket error in asyncore.py
It just thought about the fact that zope on linux seemingly does ignore when the user presses of the stop on his browser. I.e., if you access a e.g. long running python(script) and press "Stop" on the browser and you'll see the thread which runs the script is still working. Extremely ugly IMO.
Well, it also happens on win. I just looked at asyncore.dispatcher, and there's the recv() method:
def recv (self, buffer_size): try: ... except socket.error, why: # winsock sometimes throws ENOTCONN if why[0] in [ECONNRESET, ENOTCONN, ESHUTDOWN]: self.handle_close() return '' else: raise socket.error, why
The browser closes the connection when the Stop button is pressed, that means that the recieving end gets a RST, and that seems to throw a socket.error, with why[0] == ECONNRESET. Well, I know jack about the python socket class, but a strategical placed "print" tells more than a thousand words ;-).
handle_close() doesn't also do much, just logs and closes the connection. So, the poor thread never does never find out about the fact that all his work is for nothing, and happily continues.
Can't this be fixed anyhow?
cheers, oliver
_______________________________________________ Zope-Dev maillist - Zope-Dev@zope.org http://lists.zope.org/mailman/listinfo/zope-dev ** No cross posts or HTML encoding! ** (Related lists - http://lists.zope.org/mailman/listinfo/zope-announce http://lists.zope.org/mailman/listinfo/zope )
Chris McDonough wrote:
AFAIK, there is no way to "kill" a thread. On top of that, there is no way to know if a browser "stop" actually means "stop processing" (for example in the case of a long-running method, a browser stop might just mean "I went to another web page, I'll be back, continue running").
Hmm, how should the browser come back? Where to? The TCP-connection is closed. You have no way to get data back to the browser. Press the stop button, tear the network out of the wall, the thread won't stop. Maybe you say that someone may want to start a long running job per browser request, like report creation or mass mailing, but who on his right mind would implement something that way? This has the small disadvantage of scaling exactly to 4 jobs in stock zope, AFAIK. Everyone would spawn an external process/thread, not (ab)use zope threads. An already highly loaded server has much more problems this way, because it doesn't profit from users pressing the stop button. Imagine you have one slow/hanging page, maybe representing data from an overloaded sql-database connected to zope. As it is now, sooner or later all of zope's worker threads will end up hanging there, making the site inaccessible, while the users who requested the offending pages have shut down their clients long ago. Yes, I know, there may some other timeouts kick in, but the principle still stands. Apache + mod_perl don't do that, btw, and I bet a lot of other servers don't do also. cheers, oliver
On Fri, 2002-08-23 at 17:59, Oliver Bleutgen wrote:
Hmm, how should the browser come back? Where to? The TCP-connection is closed. You have no way to get data back to the browser. Press the stop button, tear the network out of the wall, the thread won't stop.
When you come back, you'd come back to another thread, but presumably the other thread would either still be running or will have finished.
Maybe you say that someone may want to start a long running job per browser request, like report creation or mass mailing, but who on his right mind would implement something that way?
Not sure if I'm in my right mind, but I cause long-running requests a fair amount. Packing a large databases, reindexing large catalogs, creating PDFs... sometimes you can't avoid it.
Imagine you have one slow/hanging page, maybe representing data from an overloaded sql-database connected to zope. As it is now, sooner or later all of zope's worker threads will end up hanging there, making the site inaccessible, while the users who requested the offending pages have shut down their clients long ago. Yes, I know, there may some other timeouts kick in, but the principle still stands.
Right, there is a real issue here. Did you read my suggestion for a new Zope feature e.g. REQUEST.RESPONSE.isClientConnected? It would let the programmer explicitly decide when to stop processing based on the connection status of the browser. Adding this feature would make it possible for a programmer to influence execution in arbitrary ways based on client TCP connection status without impacting backwards compatibility. For example with your "slow/hanging page", maybe each iteration through a loop that obtains data from the database would check the flag and stop processing if the user has gone away, e.g.: l = [] while database.next(): # pretend "slow" inner loop l.append(database.getData()) if not context.REQUEST.RESPONSE.isClientConnected(): return l return l
Apache + mod_perl don't do that, btw, and I bet a lot of other servers don't do also.
Are you sure? As far as I know, under mod_perl, the 'page' continues to be executed after the user presses the stop button. The "mod_perl developer's cookbook" also recommends something like I'm suggesting with 'isClientConnected': http://webreference.com/programming/perl/cookbook/chap4/5.html as well. - C
Chris McDonough wrote:
On Fri, 2002-08-23 at 17:59, Oliver Bleutgen wrote:
Hmm, how should the browser come back? Where to? The TCP-connection is closed. You have no way to get data back to the browser. Press the stop button, tear the network out of the wall, the thread won't stop.
When you come back, you'd come back to another thread, but presumably the other thread would either still be running or will have finished.
Yes, and where does it put his output to? There's no way to get it directly to the browser. Therefore, the typical situation is pdf-generation for later delivery (via email etc.) or whatnot, and that doesn't need to waste one of zope's worker threads, they seem too precious for me.
Maybe you say that someone may want to start a long running job per browser request, like report creation or mass mailing, but who on his right mind would implement something that way?
Not sure if I'm in my right mind, but I cause long-running requests a fair amount. Packing a large databases, reindexing large catalogs, creating PDFs... sometimes you can't avoid it.
Ok, you're completely right, sometimes you can't avoid it. This is normaly no problem, since the user knows that he will have to wait. But it's telling that 2 of your examples are administrative tasks. As for pdf-creation, I've seen a lot of people on the mailing list doing stuff like that in the background, if the creation takes too much time. I should have said "very long running tasks, which may get started relativly often". This kind of tasks where you don't need to or can't send something back to the browser.
Imagine you have one slow/hanging page, maybe representing data from an overloaded sql-database connected to zope. As it is now, sooner or later all of zope's worker threads will end up hanging there, making the site inaccessible, while the users who requested the offending pages have shut down their clients long ago. Yes, I know, there may some other timeouts kick in, but the principle still stands.
Right, there is a real issue here.
Did you read my suggestion for a new Zope feature e.g. REQUEST.RESPONSE.isClientConnected? It would let the programmer explicitly decide when to stop processing based on the connection status of the browser. Adding this feature would make it possible for a programmer to influence execution in arbitrary ways based on client TCP connection status without impacting backwards compatibility. For example with your "slow/hanging page", maybe each iteration through a loop that obtains data from the database would check the flag and stop processing if the user has gone away, e.g.:
l = [] while database.next(): # pretend "slow" inner loop l.append(database.getData()) if not context.REQUEST.RESPONSE.isClientConnected(): return l return l
I don't see backwards compatibility compromised. If the job of a method is to send its results to the browser, connection reset means it can't fullfil it's job. If you don't need to send something to the browser, you better should have offloaded the work to a special thread/process anyway. Maybe the administrative tasks you mentioned above could just ignore the connection reset instead of getting stopped, but show me someone who has pressed the stop button, reloaded or went to another page while a database pack was in progress, and I show you a very brave person. It's just super counterintuitive that the work continues after I pressed stop.
Apache + mod_perl don't do that, btw, and I bet a lot of other servers don't do also.
Are you sure? As far as I know, under mod_perl, the 'page' continues to be executed after the user presses the stop button.
The "mod_perl developer's cookbook" also recommends something like I'm suggesting with 'isClientConnected': http://webreference.com/programming/perl/cookbook/chap4/5.html as well.
I'm not sure why they talk about this, but it might have to do with the special example they give. I.e. checking if the client is connected before kicking of an expensive database query. I think their recipe is to handle the fact that the script also doesn't get stopped immediately when the connection is closed by the client, but that doesn't mean that it runs indefinately. As for if I'm sure that apache+mod_perl works different than zope, I tested that myself and wrote it to the list: http://mail.python.org/pipermail/zope/2001-October/102360.html And here's another page I just googled: "Handling the 'User pressed Stop button' case Ok, When user decided to press the stop button, apache will detect that thru the SIG{'PIPE'} and will cease the script execution. [...]" http://www.dlhoffman.com/publiclibrary/software/apache-perl-guide/obvious.ht... I don't know how old this text is, but I think we safely can assume that apache didn't get worse since then ;-). cheers, oliver
On Sat, 2002-08-24 at 13:47, Oliver Bleutgen wrote:
Yes, and where does it put his output to? There's no way to get it directly to the browser. Therefore, the typical situation is pdf-generation for later delivery (via email etc.) or whatnot, and that doesn't need to waste one of zope's worker threads, they seem too precious for me.
Well, sometimes there isn't any output or the output is not useful; there are many times you'd like to run a method for its side effects. There's no uniformly sane way to tell whether a caller has called a method for its side effects or whether they've called it in order to obtain its results.
I don't see backwards compatibility compromised. If the job of a method is to send its results to the browser, connection reset means it can't fullfil it's job.
Absolutely. The trick is being able to determine whether that is the job of the method or not, or whether the method was called for its side effects. It's obvious that more often than not a method is called for its return value rather than for its side effects. It's hard to tell when this is the case, but let's make the leap of faith and assume you could. You'll still need to figure out how to tell it to stop processing. It would seem obvious to say "just send the thread a signal to tell it to stop processing". But this isn't possible because signals in Python are always caught by the main thread. (http://www.python.org/doc/lib/module-signal.html). Additionally, even if you could send it a signal, there is no sane way to force it to stop processing. "Killing" a thread is a feature that Python doesn't have and may never have: http://sourceforge.net/tracker/index.php?func=detail&aid=221115&group_id=547... So, given that we can't kill threads or sanely do much to effect their execution from the outside, maybe we could make our threads do it cooperatively. This would require some deep dark voodoo in the Python interpreter. When a Zope worker thread got a hold of the interpreter lock, it could be instructed to check somewhere for state related to the its associated connection. If the state had a flag in it that said "stop processing", it could be tricked into executing some bytecode that caused it to raise a non-catchable exception (it would kill itself). I don't feel competent enough to write such a beast, but maybe others do. However, IMHO, it's less work and requires less explanation to make it the responsibility of the Zope programmer. Execution could be effected by requiring him to sprinkle his code with a few checks in opportune places that caused processing to stop happening if and only if ECONNRESET had been raised previously. This is why I proposed the REQUEST.RESPONSE.isClientConnected() method. It also has the benefit of being noninvasive and an add-on rather than a change to existing code.
If you don't need to send something to the browser, you better should have offloaded the work to a special thread/process anyway. Maybe the administrative tasks you mentioned above could just ignore the connection reset instead of getting stopped, but show me someone who has pressed the stop button, reloaded or went to another page while a database pack was in progress, and I show you a very brave person.
Wow, I must be a superhero then. I always knew I was in this business for the glory. ;-)
It's just super counterintuitive that the work continues after I pressed stop.
I agree. Do you have any suggestions about how to make it stop processing (conditionally) besides the one I've already given? I'm out of ideas.
I'm not sure why they talk about this, but it might have to do with the special example they give. I.e. checking if the client is connected before kicking of an expensive database query. I think their recipe is to handle the fact that the script also doesn't get stopped immediately when the connection is closed by the client, but that doesn't mean that it runs indefinately.
As for if I'm sure that apache+mod_perl works different than zope, I tested that myself and wrote it to the list:
OK, sounds reasonable... I think this may be a big win for multiprocess-model servers rather than threaded servers. It's easy to kill a process, but threads are a pain in the ass. ;-) - C
Sorry, I forgot to answer one question. Chris McDonough wrote:
Did you read my suggestion for a new Zope feature e.g. REQUEST.RESPONSE.isClientConnected? It would let the programmer explicitly decide when to stop processing based on the connection status of the browser. Adding this feature would make it possible for a programmer to influence execution in arbitrary ways based on client TCP connection status without impacting backwards compatibility. For example with your "slow/hanging page", maybe each iteration through a loop that obtains data from the database would check the flag and stop processing if the user has gone away, e.g.:
l = [] while database.next(): # pretend "slow" inner loop l.append(database.getData()) if not context.REQUEST.RESPONSE.isClientConnected(): return l return l
isClientConnected might be a good idea the same way that the abort() and other methods are a good idea in mod_perl. That is, give the developer a possibility to check if the client is still able to read, i.e. still "there". But that can't reduce the responsibility of zope in such cases IMO. How exactly would you use isClientConnected with a "results" DTML-method calling a a ZSQL-method which hangs? ;-) cheers, oliver
On Sat, 2002-08-24 at 14:00, Oliver Bleutgen wrote:
isClientConnected might be a good idea the same way that the abort() and other methods are a good idea in mod_perl. That is, give the developer a possibility to check if the client is still able to read, i.e. still "there". But that can't reduce the responsibility of zope in such cases IMO.
It's certainly not an easy problem to solve. Maybe someone on the list has suggestions. Switching to a multiprocess model might help, but that's a pretty big undertaking. I don't think its as big of a problem as you seem to believe, but then again, I'm used to it and maybe I'm inadvertently downplaying its performance impact.
How exactly would you use isClientConnected with a "results" DTML-method calling a a ZSQL-method which hangs? ;-)
There's really nothing that Zope or the programmer can do for you if a call to fetch a row from your database hangs, as it's possible (even likely) that no Python code would be executing during the time of the hang (coupled with the fact that you can't sanely kill a Python thread from outside)... I'd consider it a clue that you need to upgrade or fix your database server or at least change its configuration. - C
I know I'm late in on this thread, but I thought I'd throw in my views. I'd like to see the REQUEST be flat plain aborted when someone hits the stop button or the connection dies. I don't is the is context.REQUEST.RESPONSE.isClientConnected() really working. How would I plug this in an expensive SQL SELECT/JOIN? Why do we need this extra programming overhead? As for the long running administrative tasks, I actually see the ability to bugger off and leave them running as an extremely bad thing. Say I hit 'pack' on a big fat ZODB. I then go somewhere else. How do I now tell when its done? The only was would be to go and look at 'top' and guess which python thread is doing the pack and wait till its CPU usage drops to zero. That's pretty ropey ;-) For the same reason, I hate ZEO's pack's possibility of returning before a pack is finished. If you do a pack, I really think you should wait for the browser to return. If the browser times out, then use something like wget. If I hit 'stop', the pack should abort. As a parting example, what happens if I accidentally start a pack? How can I stop it? ;-) cheers, Chris
Chris Withers wrote:
I know I'm late in on this thread, but I thought I'd throw in my views.
This is very nice, it seemed like nobody was interested in that.
I'd like to see the REQUEST be flat plain aborted when someone hits the stop button or the connection dies.
Yes, that would be the optimal solution. Unfortunately it seems to be (nearly) impossible to do.
I don't is the is context.REQUEST.RESPONSE.isClientConnected() really working. How would I plug this in an expensive SQL SELECT/JOIN? Why do we need this extra programming overhead?
The problem seems to be that when you started the sql query, there's not much zope could do, even if it knew that the stop button was pressed - if I understand Chris correctly, that. I'm all for isClientConnected, because it gives you at least the possiblity to find out about aborted connections. This could come in handy _before_ starting long sql-queries/other expensive tasks. But, like you, I think zope should be a little bit more clever out-of-the-box at it is now.
As for the long running administrative tasks, I actually see the ability to bugger off and leave them running as an extremely bad thing. Say I hit 'pack' on a big fat ZODB. I then go somewhere else. How do I now tell when its done? The only was would be to go and look at 'top' and guess which python thread is doing the pack and wait till its CPU usage drops to zero. That's pretty ropey ;-) For the same reason, I hate ZEO's pack's possibility of returning before a pack is finished.
If you do a pack, I really think you should wait for the browser to return. If the browser times out, then use something like wget. If I hit 'stop', the pack should abort.
Hmm, can a pack always safely rolled back?
As a parting example, what happens if I accidentally start a pack? How can I stop it? ;-)
Hit the power button on the server ;-))). I did do some diggin about mod_perl and java meanwhile, maybe zope can learn something here. Mod_perl also seems _not_ to stop a long running script if this script doesn't try to write to the RESPONSE (or whatever they call it). The perl test script I posted does write output, and this causes it to stop. If I try a script with no output, it never gets stopped. Java servlets also seem only to get stopped automatically if they try to write into the RESPONSE stream of a closed connection. Buffering aside, if you try to output something in a servlet and the connection is closed, you get an exception. Im my uninformed opinion, Zope should do the same. Is this possible? cheers, oliver
Oliver Bleutgen wrote:
Mod_perl also seems _not_ to stop a long running script if this script doesn't try to write to the RESPONSE (or whatever they call it). The perl test script I posted does write output, and this causes it to stop. If I try a script with no output, it never gets stopped.
Java servlets also seem only to get stopped automatically if they try to write into the RESPONSE stream of a closed connection. Buffering aside, if you try to output something in a servlet and the connection is closed, you get an exception.
Im my uninformed opinion, Zope should do the same.
Is this possible?
Although Zope has a "response stream" method of sending information back to the client, most things in Zope don't use it. Instead, the response information is aggregated, converted into a string, and then sent back all at once at the sucessful completion of the transaction. These other systems you mention are using the availability of a response stream as a surrogate for RESPONSE.isClientConnected(). I'd rather have an explicit RESPONSE.isClientConnected(). Although, it would make sense to raise an exception if someone tried to write to the Zope response stream, in the rare cases when this does happen. -- Steve Alexander
Steve Alexander wrote:
Oliver Bleutgen wrote:
Although Zope has a "response stream" method of sending information back to the client, most things in Zope don't use it.
Instead, the response information is aggregated, converted into a string, and then sent back all at once at the sucessful completion of the transaction.
Well, then it may be difficult to get zope to behave like the "other" systems, right?
These other systems you mention are using the availability of a response stream as a surrogate for RESPONSE.isClientConnected(). I'd rather have an explicit RESPONSE.isClientConnected().
This is not correct. The systems I mentioned do also have the possibility to check if the client is still connected, it's just that they additionally prevent writing to a response which will never get delivered. This is quite reasonable IMO.
Although, it would make sense to raise an exception if someone tried to write to the Zope response stream, in the rare cases when this does happen.
I know we are talking about extreme cases here. But the behavior of java and mod_perl may prevent the whole system from falling over on a loaded server. This is made worse by the relative small number of threads which are running on a stock zope. cheers, oliver
Chris, Do you have any implementation suggestions? - C On Wed, 2002-08-28 at 07:49, Chris Withers wrote:
I know I'm late in on this thread, but I thought I'd throw in my views.
I'd like to see the REQUEST be flat plain aborted when someone hits the stop button or the connection dies.
I don't is the is context.REQUEST.RESPONSE.isClientConnected() really working. How would I plug this in an expensive SQL SELECT/JOIN? Why do we need this extra programming overhead?
As for the long running administrative tasks, I actually see the ability to bugger off and leave them running as an extremely bad thing. Say I hit 'pack' on a big fat ZODB. I then go somewhere else. How do I now tell when its done? The only was would be to go and look at 'top' and guess which python thread is doing the pack and wait till its CPU usage drops to zero. That's pretty ropey ;-) For the same reason, I hate ZEO's pack's possibility of returning before a pack is finished.
If you do a pack, I really think you should wait for the browser to return. If the browser times out, then use something like wget. If I hit 'stop', the pack should abort.
As a parting example, what happens if I accidentally start a pack? How can I stop it? ;-)
cheers,
Chris
On Wed, 2002-08-28 at 07:49, Chris Withers wrote:
I'd like to see the REQUEST be flat plain aborted when someone hits the stop button or the connection dies.
Thats probably impossible if there is an HTTP proxy between your browser and zope.
I don't is the is context.REQUEST.RESPONSE.isClientConnected() really working. How would I plug this in an expensive SQL SELECT/JOIN? Why do we need this extra programming overhead?
As for the long running administrative tasks, I actually see the ability to bugger off and leave them running as an extremely bad thing. Say I hit 'pack' on a big fat ZODB. I then go somewhere else. How do I now tell when its done. The only was would be to go and look at 'top' and guess which python thread is doing the pack and wait till its CPU usage drops to zero. That's pretty ropey ;-)
Yes, all good storages use zLOG to report that they are finished packing. ;-) On Wednesday 28 Aug 2002 2:47 pm, Chris McDonough wrote:
Do you have any implementation suggestions?
On posix there may be some SIGINT-based options, which Im sure would expose many other weaknesses. On Win32, Im fairly sure we are out of luck for interruption without RESPONSE.isClientConnected()
Toby Dickenson wrote:
On Wed, 2002-08-28 at 07:49, Chris Withers wrote:
I'd like to see the REQUEST be flat plain aborted when someone hits the stop button or the connection dies.
Thats probably impossible if there is an HTTP proxy between your browser and zope.
Why? It seems logical for a proxy to also reset the connection to the backend server when he looses the connection to the client. I haven't verified this, though. cheers, oliver
On Wednesday 28 Aug 2002 4:04 pm, Oliver Bleutgen wrote:
Toby Dickenson wrote:
On Wed, 2002-08-28 at 07:49, Chris Withers wrote:
I'd like to see the REQUEST be flat plain aborted when someone hits the stop button or the connection dies.
Thats probably impossible if there is an HTTP proxy between your browser and zope.
Why? It seems logical for a proxy to also reset the connection to the backend server when he looses the connection to the client. I haven't verified this, though.
By 'impossible' I meant that it is impossible to *guarantee* that the proxy will disconnect in the way that Chris wants. In some cases it will, but it is certainly not required. Squid will hold on for a while until it has got all the headers, and will hold on indefinitely if it has nearly downloaded the whole response.
Chris McDonough wrote:
Chris,
Do you have any implementation suggestions?
Find a pub and make myself less sober? LSD also springs to mind for the thinks-laterally-department... *grinz* Seriously, no, but I hope this is something that's been raised on the Zope 3 radar... Chris
FWIW, I just checked in a signal handler to the trunk that makes it possible on UNIX to do "kill -USR1 `cat var/Z2.pid`" and pack the database to 0 days, so all this business about packing TTW and whether you need to let the browser wait or not is for naught. ;-) On Wed, 2002-08-28 at 07:49, Chris Withers wrote:
I know I'm late in on this thread, but I thought I'd throw in my views.
I'd like to see the REQUEST be flat plain aborted when someone hits the stop button or the connection dies.
I don't is the is context.REQUEST.RESPONSE.isClientConnected() really working. How would I plug this in an expensive SQL SELECT/JOIN? Why do we need this extra programming overhead?
As for the long running administrative tasks, I actually see the ability to bugger off and leave them running as an extremely bad thing. Say I hit 'pack' on a big fat ZODB. I then go somewhere else. How do I now tell when its done? The only was would be to go and look at 'top' and guess which python thread is doing the pack and wait till its CPU usage drops to zero. That's pretty ropey ;-) For the same reason, I hate ZEO's pack's possibility of returning before a pack is finished.
If you do a pack, I really think you should wait for the browser to return. If the browser times out, then use something like wget. If I hit 'stop', the pack should abort.
As a parting example, what happens if I accidentally start a pack? How can I stop it? ;-)
cheers,
Chris
_______________________________________________ Zope-Dev maillist - Zope-Dev@zope.org http://lists.zope.org/mailman/listinfo/zope-dev ** No cross posts or HTML encoding! ** (Related lists - http://lists.zope.org/mailman/listinfo/zope-announce http://lists.zope.org/mailman/listinfo/zope )
participants (5)
-
Chris McDonough -
Chris Withers -
Oliver Bleutgen -
Steve Alexander -
Toby Dickenson