[Desktop_printing] CUPS code quality ...
Norm Jacobs
Norm.Jacobs at Sun.COM
Thu Feb 2 11:19:55 PST 2006
It also sounds like asynchronous interfaces for print queue enumeration,
status query, and job submission are likely to be of interest in a future
revision.
-Norm
McDonald, Ira wrote:
>Hi,
>
>[for context] PAPI is the FSG Open Printing standard PAPI/1.0 (July
>2005) at:
>
>ftp://ftp.pwg.org/pub/pwg/fsg/spool/papi-v1.0-2005-07-15.pdf
>
>There have been complete open source reference implementations for
>several years. Norm Jacobs (Sun, chair of FSG Open Printing) has
>PAPI supporting IPP and LPR on Solaris and is the principal current
>
>
FWIW, It also builds and runs on Linux. I suspect that the same
could be said for several platforms with POSIX interfaces.
>maintainer of the PAPI specification and source.
>
>Input for extensions to PAPI/1.0 is welcome (we're looking at
>Capabilities now, which means PPD parsing, etc.).
>
>
>Cheers,
>- Ira (for FSG/OP International Steering Committee)
>
>
>Ira McDonald (Musician / Software Architect)
>Blue Roof Music / High North Inc
>PO Box 221 Grand Marais, MI 49839
>phone: +1-906-494-2434
>email: imcdonald at sharplabs.com
>
>
>
>>-----Original Message-----
>>From: desktop_printing-bounces at lists.osdl.org
>>[mailto:desktop_printing-bounces at lists.osdl.org]On Behalf Of michael
>>meeks
>>Sent: Thursday, February 02, 2006 12:24 PM
>>To: Michael Sweet
>>Cc: desktop_printing at lists.osdl.org
>>Subject: Re: [Desktop_printing] CUPS code quality ...
>>
>>
>>Hi Michael,
>>
>> Good name incidentally :-)
>>
>>On Thu, 2006-02-02 at 08:35 -0500, Michael Sweet wrote:
>>
>>
>>>I'll address these points in reverse:
>>>
>>>
>> Well, it seems you're at beginning to consider some of
>>these issues in
>>1.2 that's great.
>>
>>
>>
>>> 1. Thread safety; CUPS 1.2 is thread-safe, and
>>>
>>>
>>threading support
>>
>>
>>> is enabled by default. Previous releases were
>>>
>>>
>>thread safe if
>>
>>
>>> you stayed away from the convenience functions that
>>>
>>>
>>used shared
>>
>>
>>> global data.
>>>
>>>
>> Ok - well, I guess thread local storage is one way
>>around returning
>>const static internal buffers - although the gotcha for the
>>unwary still
>>remains. Clearly having a unified, standard calling convention for
>>methods helps substantially wrt. learning the API, not
>>leaking/crashing
>>and auto-generated scripting bindings to name a few.
>>
>>
>>
>>> 2. Synchronous; only if you use the convenience APIs -
>>>
>>>
>>we use the
>>
>>
>>> CUPS API as the basis for cupsd, and all of the HTTP and IPP
>>> APIs operate as FSMs so that we can handle multiple
>>>
>>>
>>connections
>>
>>
>>> asynchronously. Other apps can do this or use
>>>
>>>
>>threads as they
>>
>>
>>> like.
>>>
>>>
>> So - can you write me a short code-fragment, with error
>>checking,
>>partial write support etc. that would asynchronously fetch a PPD ? (no
>>commenting necessary ;-) [ also some idea of how to integrate
>>that into
>>a custom mainloop - just need a socket to poll on etc. ] [
>>I'm assuming
>>here there is an API function to handle a partial read in
>>response to a
>>POLLIN|POLLPRI input, and O_NONBLOCK socket handling ? ]
>>
>>
>>
>>> 2a. Variable time; them's the breaks, some people have fast
>>> systems, some slow, some people use local servers, some
>>> remote, some people have 1 printer, some have 10000.
>>>
>>>
>> Sure - so clearly synchronous APIs are a disaster
>>waiting to happen -
>>particularly without threading support; but of course this is
>>now fixed
>>in 1.2 :-) [ which I don't seem to have on my bleeding-edge
>>system: odd,
>>so if you don't mind I'll refer below to what I see in 1.1 ].
>>
>>
>>
>>> 4. Badly designed; also a matter of opinion, although
>>>
>>>
>>I heartily
>>
>>
>>> agree that certain parts of the API are showing
>>>
>>>
>>their age and
>>
>>
>>> could use a redesign. This, unfortunately, is a challenge
>>> with the parallel goal of maintaining backwards
>>>
>>>
>>compatibility
>>
>>
>>> in the 1.x series, but we welcome feedback in improving the
>>> existing APIs and adding new ones that match
>>>
>>>
>>developer usage.
>>
>> Of course back-compat (particularly with this legacy)
>>is a pain; but -
>>then the problems appear to be legion - an entirely new API (perhaps
>>built on top of CUPS initially) would not have this problem of course.
>>
>>
>>
>>>You *do* know that you can do a CUPS_GET_PRINTERS request with a
>>>non-blocking HTTP connection to collect the data as you go along,
>>>right? And use requested-attributes to reduce the amount of data
>>>that is processed?
>>>
>>>
>> No - I have no idea; it's not clear from the headers,
>>it's not clear to
>>me how to do it. When I read the code in days of yore & the
>>IPP spec. it
>>was not clear to me how to construct arbitrary
>>strings/URIs/etc./whatever & use the raw CUPS http code to achieve
>>anything.
>>
>>
>>
>>>Comments like this are not useful. Simple documentation = easier to
>>>follow code (and clearly you are able to follow the CUPS
>>>
>>>
>>code, even if
>>
>>
>>>you don't like the design...)
>>>
>>>
>> I spend my life reading code of often very mixed
>>quality - some of the
>>worst is unfortunately my own :-) perhaps you'd accept a patch to CUPS
>>from me ? ;-)
>>
>>
>>
>>>We use static and dynamic buffers when they make sense.
>>>Sockets are a requirement for using IPP over HTTP.
>>>
>>>
>> When does a static buffer make sense ? seriously; this
>>is my point -
>>network traffic is slow, IPC is slow - strdup() is
>>blindingly, amazingly
>>fast. My point is - why use static buffers all over the place
>>(which may
>>give some marginal speed win) when they also tend to have well known
>>security issues and of course introduce arbitrary fixed
>>limits all over
>>the show: thus either wasting memory unnecessarily or limiting
>>longer-string corner cases.
>>
>>
>>
>>>It is synchronous by design, but nothing prevents you from using the
>>>HTTP and IPP APIs to do this asynchronously - you'll just need to
>>>do the IPP queries and then the HTTP GET yourself.
>>>
>>>
>> So again; when I tried to do this some year ago - it
>>was extremely
>>unclear how to do that; but perhaps I was just missing
>>something simple.
>>
>>
>>
>>>> Of course, what the contract is wrt. this file -
>>>>
>>>>
>>whether you get to
>>
>>
>>>>unlink it later at your leisure, or what is being really
>>>>
>>>>
>>passed to you
>>
>>
>>>>is (AFAICS) not explicit in the commenting. An opaque handle with
>>>>clearly defined & consistent lifecycle semantics would clearly be
>>>>better.
>>>>
>>>>
>>>We're not trying to define a whole new object/abstraction interface
>>>here (i.e. no glib or similar frameworks), just trying to get the
>>>job done.
>>>
>>>
>> Well; I'm not asking for rocket science here - just
>>expecting some of
>>the hard-learned lessons of maintaining stable ABIs over long
>>periods to
>>hit home.
>>
>> So - lets take another example:
>>
>>typedef struct
>>{
>> int fd; /* File descriptor for
>>this socket */
>> ...
>> char fields[HTTP_FIELD_MAX][HTTP_MAX_VALUE];
>> ...
>> /**** New in CUPS 1.1.19 ****/
>> fd_set *input_set; /* select() set for
>>httpWait() */
>> http_status_t expect; /* Expect: header */
>> char *cookie; /* Cookie value(s) */
>> /**** New in CUPS 1.1.20 ****/
>> char authstring[HTTP_MAX_VALUE],
>> /* Current
>>Authentication value */
>> userpass[HTTP_MAX_VALUE];
>> /* Username:password string */
>> int digest_tries; /* Number of tries for
>>digest auth */
>>} http_t;
>>
>> So several things to say here: exposing all this lot:
>>
>> * gives you no guarantees of encapsulation, anything can
>> screw with any of the fields at any time in any thread,
>> in uncontrolled ways; resulting in inconsistent state.
>>
>> * gives you expansion problems: yes, I'm sure you're not
>> expecting people to stack/heap allocate this structure
>> themselves (when compiling with some old headers) - but
>> someone, somewhere is going to do this if they can.
>>
>> * gives you hard-to-detect versioning problems; if I
>> compile vs. 1.1.20 and run with 1.1.19 - and try to
>> access 'authstring' which was there in the header when
>> I compiled: what happens ?
>> * the alternative being a linker error when an
>> non-available accessor is called; or a named /
>> enumerated property is not available.
>>
>> A far better way is to have an opaque structure, and either
>>named/enumerated properties - or individual accessors. If you're
>>particularly 31337 you can not dup strings and return 'const char*'
>>pointers to the internal representation, thus saving a
>>handful of cycles
>>and exposing yourself to a different set of problems later :-)
>>
>> Things like:
>>
>> char fields[HTTP_FIELD_MAX][HTTP_MAX_VALUE];
>>
>> are particularly odd; HTTP_FIELD_MAX is 28 for me ( the
>>end of an
>>enumeration ) of course - it's not really possible to extent that
>>enumeration without breaking the entire http_t layout ;-) but
>>that aside
>>we have:
>>
>># define HTTP_MAX_URI 1024 /* Max length
>>of URI string */
>># define HTTP_MAX_HOST 256 /* Max length
>>of hostname string */
>># define HTTP_MAX_BUFFER 2048 /* Max length of data buffer */
>># define HTTP_MAX_VALUE 256 /* Max header field
>>value length */
>>
>> So fields consumes 7kb, and other bits perhaps another
>>2 1/2k or so,
>>size-wise not important, but most of that will be wasted most of the
>>time.
>>
>>
>>
>>>> Is this symptomatic of a wider problem ? no idea. All I
>>>>
>>>>
>>know is my
>>
>>
>>>>experience of using the client code & trying to make it do what is
>>>>necessary has been poor in the extreme: of course, it's entirely
>>>>possible that is just down to my ignorance.
>>>>
>>>>
>>>In my experience, too many developers jump to conclusions about
>>>software before asking the people who wrote the software...
>>>
>>>
>> Well - fine; what is your ABI compatibility strategy
>>here ? why is
>>there no encapsulation/data-hiding ? why are there all these arbitrary
>>limits around the place ? can a FIELD really not contain a
>>URI ? ( field
>>max 256chars, URI max 1024 chars ).
>>
>> Of course perhaps my critique is unfair - I've only had
>>to use the API
>>in quite a minor way; but the above are my questions from reading the
>>http.h header; from a quick scan similar problems are elsewhere too.
>>
>>
>>
>>>There is already an API like this: PAPI. However, you may be
>>>disappointed to discover that PAPI's APIs are all
>>>
>>>
>>synchronous, with no
>>
>>
>>>possibility of asynchronous operation. PAPI also suffers from the
>>>"least common denominator" syndrome, but that will likely
>>>
>>>
>>improve over
>>
>>
>>>time...
>>>
>>>
>> Interesting; of course - as you see I'm almost totally
>>ignorant of
>>printing as a domain :-) perhaps PAPI is what I'm looking
>>for. ie. if I
>>was going to bless an API/ABI to have to support for the next
>>20 years &
>>remain compatible with - I would not be picking CUPS in it's current
>>state. Of course as you say, that's a personal opinion, and
>>perhaps I'm
>>deluded & you'll persuade me otherwise :-) I look forward to that
>>process.
>>
>> Thanks for listening,
>>
>> Regards,
>>
>> Michael.
>>
>>--
>> michael.meeks at novell.com <><, Pseudo Engineer, itinerant idiot
>>
>>
>>
>>
More information about the Printing-summit
mailing list