[squid-dev] [PATCH] PackableStream for cachemgr reports

Amos Jeffries squid3 at treenet.co.nz
Sat Aug 8 08:03:52 UTC 2015


On 8/08/2015 5:04 p.m., Alex Rousskov wrote:
> On 08/05/2015 10:24 AM, Amos Jeffries wrote:
>> Adds a PackableStream class which provides std::ostream semantics for
>> writing data to a cachemgr report.
> 
> 
>> +    PackableStreamBuf(Packable *out) : outBuf(out) {}
> 
> Missing "explicit" or a comment justifying implicit conversions.
> 
> 
>> +    ~PackableStreamBuf() {}
> 
> Please add "virtual".
> 

Fixed both. Darn cut-n-paste.

Also fixing the StoreEntryStream.h where these originated.

> 
>> +    virtual int_type overflow(int_type aChar = traits_type::eof()) {
> 
>> +    virtual int sync() {
> 
>> +    virtual std::streamsize xsputn(const char * chars, std::streamsize number) {
> 
> Please add "override" to each overridden virtual method from the
> std::streambuf API.

I dont quite follow you here.

Is that a request to add comments?
 or an attribute I was not aware of?

> 
>> +            // NP: cast because GCC promotes int_type to 32-bit type
>> +            //     std::basic_streambuf<char>::int_type {aka int}
>> +            //     despite the definition with 8-bit type value.
>> +            char chars[1] = {char(aChar)};
> 
> Please use static_cast (or another appropriately named cast) to cast.
> 
> Do not declare "chars" when you do not need them.

The comment says 'cast' because that is essentially what is being done.

However the char(int_type) type constructor is necessary because this is
the fundamental int_type being passed in. All the available
*_cast<char>() operators are built to convert a 32-bit int_type value
into an array of 4 bytes before dealing with the byte->char conversion
part. Which then incorrectly takes the lowest-8 bits rather than the
first-8.

The int_type being passed in actually has only 8-bits of length, not
32-bits. Which screws over those cast operators memory accesses. They
typically end up casting the value at 2nd or 4th byte of a 1-byte stack
object to char.


If you are aware of a foo_cast<>() operator that correctly converts in
the presence of that traits weirdness I am interested.


> 
> Make "chars" constant if you can.

Trying.

> 
>> +                outBuf->append(chars, 1);
> ...
>> +            outBuf->append(pbase(), pending);
> ...
>> +            outBuf->append(chars, number);
> 
> To reduce code duplication and to assist with debugging, it would be
> nice to move these three into a single dedicated private write() method
> or similar.
> 

Calling xsputn() instead now. Although note that trampolining off a
virtual function (non-inlinable in some compilers) actually makes it
less efficient.


> 
>> +        if (pending)
>> +            outBuf->append(pbase(), pending);
> ...
>> +        if (number)
>> +            outBuf->append(chars, number);
> 
> Are you sure you are saving more cycles by bypassing zero-size appends
> than you are wasting on the size check? If you are not, do not waste
> cycles and ink on that check.

The MemBuf and SBuf implementations of append() check for 0 themselves.
But the StoreEntry assumed non-0 and does some relatively heavy
allocation. PackableStream(StoreEntry) is expected to be the common use
case so this is a net performance gain until StoreEntry itself gets
optimized.


> 
>> +private:
>> +    Packable *outBuf;
>> +};
> 
> Why is this a pointer? The class code seems to require a non-null
> pointer. If you can make this a reference, please do.
> 
> If this member remains a pointer, we ought to check that it is not null.

It was pointer in StoreEntyStream. Trying reference.

> 
>> +    PackableStream(Packable *entry): std::ostream(0), theBuffer(entry) {
> 
> Missing "explicit" or a comment justifying implicit conversions.

Fixed.

> 
> If you do not want this class to be responsible for checking "entry" for
> being nil (after the PackableStreamBuf fixes above), then use a
> reference instead of a pointer.
> 
> Please s/entry/packable/ or something like that. We should not imply
> that this is StoreEntry even if it [usually] is today.

Nod. Using p like everywhere else.

> 
> 
>> +    /* create a stream for writing text etc into theEntry */
> 
> No theEntry here.

Fixed.

>> +    virtual void dump(std::ostream &) {}
> 
> This should either throw (if it must not be called unless overridden) or
> provide some debugging (if it is OK to leave it a no-op).
> 

It is okay to be a no-op. It should not be called if not overridden.
Making it dump "X is NOT Implemented" to the stream for now.


> 
>> +    virtual void dump(std::ostream &) {}
>> +
>> +    // magic wrapper.
>> +    // old code will override this instead of the new ostream dumper.
>> +    virtual void dump(StoreEntry *e);
> 
> IIRC, it is not possible to override just one dump() without hiding the
> other. I do not know whether we enable compiler warnings about hiding
> virtual methods by default, but we _should_ enable them if we do not
> already.

I thought both symbols had a vtable entry under their different mangling.

Running some extra tests to check that now.


> 
> Perhaps more importantly, why do we actually need this [black] magic
> here? Can the new method just use a different name, like dumpToStream(),
> until the transition is over?

The only black magic bit is if both virtuals get hidden when one is
overridden. And that is anti-useful. So if it happens I will definitely
rename.

Otherwise it is six of one, half a dozen of the other.

> 
>> * convert old Action classes to new API
>> * refactor old C-code report generators to be Action classes
>>  - fixing display output syntax to minimal YAML as we go on both the
>> above. It mostly is already, but some reports have wrong syntax.
> 
> It feels wrong that you want Actions to output using strict syntax but
> give them an "free form" or "anything goes" std::ostream as a tool to
> accomplish that. Should not Action dumping methods receive a specialized
> class that makes it easy to produce the output you want and hard (if not
> impossible) to make syntax mistakes?
> 

Action itself is currently that class you speak about AFAICT. Maybe in
future the "update Action API to receive client desired format" will be
a formatter class rather than just header value.

For now its just a free-form mix in plain text. To which a stream is
suited. And possible something the formatter classes could use better
than a direct Packable object pointer/reference.

Given the 4 years it has taken to get this far I'm expecting the stream
and cut-down YAML will be used for a long time before we get around to
debating better formatter syntax(es) again.


> 
> Finally, "make check" fails in my tests with several errors like:
> 
> 
>> tests/stub_libmgr.cc:65:6: error: prototype for ‘void Mgr::RotateAction::dump(StoreEntry*)’ does not match any in class ‘Mgr::RotateAction’
>>  void Mgr::RotateAction::dump(StoreEntry *entry) STUB
>>       ^
>> In file included from tests/stub_libmgr.cc:48:0:
>> ../src/mgr/BasicActions.h:78:18: error: candidate is: virtual void Mgr::RotateAction::dump(std::ostream&)
>>      virtual void dump(std::ostream &);
>>                   ^
> 

Fixing.

Followup patch to come when the virtual experiments and re-tests are
completed.

Amos



More information about the squid-dev mailing list