Skip to content

BUG: Initialize member variables#2347

Merged
thewtex merged 1 commit intoInsightSoftwareConsortium:masterfrom
jhlegarreta:FixUninitializedVariables
Mar 4, 2021
Merged

BUG: Initialize member variables#2347
thewtex merged 1 commit intoInsightSoftwareConsortium:masterfrom
jhlegarreta:FixUninitializedVariables

Conversation

@jhlegarreta
Copy link
Copy Markdown
Member

Initialize member variables.

Fixes:

UMC ==4407== Conditional jump or move depends on uninitialised value(s)
==4407==    at 0xC3A9316:
double_conversion::DoubleToStringConverter::ToShortestIeeeNumber(double,
double_conversion::StringBuilder*,
double_conversion::DoubleToStringConverter::DtoaMode) const
(double-to-string.cc:172)
==4407==    by 0x6835CE6:
double_conversion::DoubleToStringConverter::ToShortest(double,
double_conversion::StringBuilder*) const (double-to-string.h:166)
==4407==    by 0x68354F6: (anonymous
namespace)::ConvertToShortest(double_conversion::DoubleToStringConverter
const&, double, double_conversion::StringBuilder&)
(itkNumberToString.cxx:31)
==4407==    by 0x68355F8: std::string (anonymous
namespace)::FloatingPointNumberToString(double) (itkNumberToString.cxx:55)
==4407==    by 0x683555B: itk::NumberToString::operator()(double) const
(itkNumberToString.cxx:71)
==4407==    by 0x6834EC5: std::ostream& itk::operator<< (std::ostream&,
itk::Array const&) (itkArrayOutputSpecialization.cxx:37)
==4407==    by 0x250C46: itk::DirectedHausdorffDistanceImageFilter,
itk::Image >::PrintSelf(std::ostream&, itk::Indent) const
(itkDirectedHausdorffDistanceImageFilter.hxx:222)
==4407==    by 0x67FBB8D: itk::LightObject::Print(std::ostream&,
itk::Indent) const (itkLightObject.cxx:125)
==4407==    by 0x24E25F: itkDirectedHausdorffDistanceImageFilterTest(int,
char**) (itkDirectedHausdorffDistanceImageFilterTest.cxx:60)
==4407==    by 0x1D634B: main (ITKDistanceMapTestDriver.cxx:217)

and

UMC ==5645== Conditional jump or move depends on uninitialised value(s)
==5645==    at 0x927FDB0: std::ostreambuf_iterator > std::num_put >
>::_M_insert_int(std::ostreambuf_iterator >, std::ios_base&, char,
>unsigned long) const (in /usr/lib64/libstdc++.so.6.0.19)
==5645==    by 0x928004C: std::num_put >
>::do_put(std::ostreambuf_iterator >, std::ios_base&, char, unsigned long)
>const (in /usr/lib64/libstdc++.so.6.0.19)
==5645==    by 0x928C2ED: std::ostream& std::ostream::_M_insert(unsigned
long) (in /usr/lib64/libstdc++.so.6.0.19)
==5645==    by 0x24F2B6: std::ostream& itk::operator<< <2u>(std::ostream&,
itk::Size<2u> const&) (itkSize.h:412)
==5645==    by 0x2C6068: itk::ImagePCAShapeModelEstimator, itk::Image
>::PrintSelf(std::ostream&, itk::Indent) const
>(itkImagePCAShapeModelEstimator.hxx:52)
==5645==    by 0x6DBFB8D: itk::LightObject::Print(std::ostream&,
itk::Indent) const (itkLightObject.cxx:125)
==5645==    by 0x2C2F09: itkImagePCAShapeModelEstimatorTest(int, char**)
(itkImagePCAShapeModelEstimatorTest.cxx:141)
==5645==    by 0x20DA6B: main (ITKImageStatisticsTestDriver.cxx:272)

and

UMC ==6426== Conditional jump or move depends on uninitialised value(s)
==6426==    at 0x10E3942C: __printf_fp_l (in /usr/lib64/libc-2.17.so)
==6426==    by 0x10E38526: vfprintf (in /usr/lib64/libc-2.17.so)
==6426==    by 0x10E63178: vsnprintf (in /usr/lib64/libc-2.17.so)
==6426==    by 0x1095263C: ??? (in /usr/lib64/libstdc++.so.6.0.19)
==6426==    by 0x10958CA3: std::ostreambuf_iterator > std::num_put >
>::_M_insert_float(std::ostreambuf_iterator >, std::ios_base&, char, char,
>double) const (in /usr/lib64/libstdc++.so.6.0.19)
==6426==    by 0x10958F8F: std::num_put >
>::do_put(std::ostreambuf_iterator >, std::ios_base&, char, double) const
>(in /usr/lib64/libstdc++.so.6.0.19)
==6426==    by 0x10964B94: std::ostream& std::ostream::_M_insert(double)
(in /usr/lib64/libstdc++.so.6.0.19)
==6426==    by 0x738573: itk::MultiphaseSparseFiniteDifferenceImageFilter,
itk::Image, itk::Image, itk::ScalarChanAndVeseLevelSetFunction,
itk::Image, itk::ConstrainedRegionBasedLevelSetFunctionSharedData,
itk::Image, itk::ScalarChanAndVeseLevelSetFunctionData, itk::Image > > >,
unsigned int>::PrintSelf(std::ostream&, itk::Indent) const
(itkMultiphaseSparseFiniteDifferenceImageFilter.hxx:1421)
==6426==    by 0xE497B8D: itk::LightObject::Print(std::ostream&,
itk::Indent) const (itkLightObject.cxx:125)
==6426==    by 0x736F6D:
itkScalarChanAndVeseSparseLevelSetImageFilterTest1(int, char**)
(itkScalarChanAndVeseSparseLevelSetImageFilterTest1.cxx:54)
==6426==    by 0x331ADE: main (ITKReviewTestDriver.cxx:327)

UMC ==6426== Conditional jump or move depends on uninitialised value(s)
==6426==    at 0x10957DB0: std::ostreambuf_iterator > std::num_put >
>::_M_insert_int(std::ostreambuf_iterator >, std::ios_base&, char,
>unsigned long) const (in /usr/lib64/libstdc++.so.6.0.19)
==6426==    by 0x1095804C: std::num_put >
>::do_put(std::ostreambuf_iterator >, std::ios_base&, char, unsigned long)
>const (in /usr/lib64/libstdc++.so.6.0.19)
==6426==    by 0x109642ED: std::ostream& std::ostream::_M_insert(unsigned
long) (in /usr/lib64/libstdc++.so.6.0.19)
==6426==    by 0x7385BB: itk::MultiphaseSparseFiniteDifferenceImageFilter,
itk::Image, itk::Image, itk::ScalarChanAndVeseLevelSetFunction,
itk::Image, itk::ConstrainedRegionBasedLevelSetFunctionSharedData,
itk::Image, itk::ScalarChanAndVeseLevelSetFunctionData, itk::Image > > >,
unsigned int>::PrintSelf(std::ostream&, itk::Indent) const
(itkMultiphaseSparseFiniteDifferenceImageFilter.hxx:1422)
==6426==    by 0xE497B8D: itk::LightObject::Print(std::ostream&,
itk::Indent) const (itkLightObject.cxx:125)
==6426==    by 0x736F6D:
itkScalarChanAndVeseSparseLevelSetImageFilterTest1(int, char**)
(itkScalarChanAndVeseSparseLevelSetImageFilterTest1.cxx:54)
==6426==    by 0x331ADE: main (ITKReviewTestDriver.cxx:327

signaled at:
https://open.cdash.org/viewDynamicAnalysis.php?buildid=7070588

PR Checklist

@jhlegarreta jhlegarreta added type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances area:Filtering Issues affecting the Filtering module labels Mar 1, 2021

Array<RealType> m_MaxDistance;
Array<IdentifierType> m_PixelCount;
Array<IdentifierType> m_PixelCount = Array<RealType>::Fill(NumericTraits<IdentifierType>::ZeroValue());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They (m_PixelCount, m_MaxDistance, m_Sum) are initialized to size of number of threads in ::BeforeThreadedGenerateData() . Here they have zero size. I guess this can not compile too, AFAIK.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right; I had a look at BeforeThreadedGenerateData() but here I assumed that the same thinking could not be applied. At the same time, I questioned myself about the dynamic analysis warning if the array has no allocated size at this point/when calling it for a newly created class instance to print its members. I will need to investigate more.

Comment thread Modules/Filtering/DistanceMap/include/itkDirectedHausdorffDistanceImageFilter.h Outdated

Array<RealType> m_MaxDistance;
Array<IdentifierType> m_PixelCount;
Array<IdentifierType> m_PixelCount = Array<RealType>::Fill(NumericTraits<IdentifierType>::ZeroValue());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this. This is in the class declaration, for a non-static member so the initialization here is the default for any constructor that doesn't explicitly give an initialization for this member in the : member0(value0), member1(value1) part. But the old code with the zero-number-of-arguments constructor did so with that constructor; so I don't understand why this is better. Also, the right hand side is invoking Fill without a this pointer available, which I don't understand since Fill is not a static method.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, calling Fill was not correct. The old code is producing warnings in the dynamic analysis. As said before, I ignore why since the array has no allocated size. Explicitly initialized to a zero size in ddd0e27.

Comment thread Modules/Filtering/ImageStatistics/include/itkImagePCAShapeModelEstimator.h Outdated
@jhlegarreta jhlegarreta force-pushed the FixUninitializedVariables branch 2 times, most recently from ddd0e27 to afee6cc Compare March 1, 2021 20:02
Copy link
Copy Markdown
Contributor

@Leengit Leengit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jhlegarreta jhlegarreta force-pushed the FixUninitializedVariables branch from afee6cc to 0fa8eba Compare March 1, 2021 20:59
@issakomi
Copy link
Copy Markdown
Member

issakomi commented Mar 1, 2021

I am trying with simple own test

#include "itkDirectedHausdorffDistanceImageFilter.h"
#include <iostream>

int main(int, char**)
{
  using ImageType = itk::Image<unsigned char, 2>;
  using FilterType = itk::DirectedHausdorffDistanceImageFilter<ImageType, ImageType>;
  typename FilterType::Pointer filter = FilterType::New();
  std::cout << filter << std::endl;
  return 0;
}

valgrind --leak-check=full ./test

and Valgrind defects are still here, with Array<IdentifierType> m_PixelCount{};
And also with Array<RealType> m_MaxDistance{};
Commenting both m_MaxDistance and m_PixelCount in PrintSelf() function removes Valgrind errors.
So i am afraid Valgrind errors will not go from dashboard.

BTW simple

#include <itkArray.h>
#include <iostream>

int main(int, char**)
{
  itk::Array<int> x;
  std::cout << x << std::endl;
  return 0;
}

doesn't produce any Valgrind error. Prints [] without any errors. I am not sure. I'll look again later, if errors will be on dashboard, it is interesting.

@jhlegarreta
Copy link
Copy Markdown
Member Author

jhlegarreta commented Mar 1, 2021

@issakomi thanks for trying the fixes. Highly appreciated 💯.

I think removing the variables from the PrintSelf method is not the direction in which we wish to move: all member variables have to be initialized to known values to avoid cases where omitting these result in unexpected/undocumented behavior. Testing the print methods through the ITK_EXERCISE_BASIC_OBJECT_METHODS and the dynamic analysis are good tools to detect such omissions. Even if this leads us to greater, time consuming development efforts. The effort is worthwhile in the long run. Hope this makes sense 👍,

If the current fix does not work, then other parts of the code where the same convention is used to initialize an itk::Array might suffer from the same issue I'd say. Worrying. And in any case, not sure why that happens, since the array has zero size, and had zero size even before the proposed fix 😔 (personally, when I looked at this I was more confused to see that no defect is detected for m_MaxDistance, which has size 1 and its value is not initialized, but that can be left for another battle/PR).

What if instead of the current fix the following is done in the constructor?

m_PixelCount.SetSize(this->GetNumberOfWorkUnits());
m_PixelCount.Fill(NumericTraits<IdentifierType>::ZeroValue());

@issakomi
Copy link
Copy Markdown
Member

issakomi commented Mar 1, 2021

  m_MaxDistance.SetSize(this->GetNumberOfWorkUnits());
  m_MaxDistance.Fill(NumericTraits<RealType>::ZeroValue());
  m_PixelCount.SetSize(this->GetNumberOfWorkUnits());
  m_PixelCount.Fill(NumericTraits<IdentifierType>::ZeroValue());

setting this in constructor (also removed {} and : m_MaxDistance(1)) removes all Valgrind errors.

Edit: only exactly like above, if you let : m_MaxDistance(1) and initialize only m_PixelCount - errors are still here. Ohh.

Edit:
Sorry, i don't think PrintSelf() it is a kind of in deep debug, IMHO it is just to see parameters, defaults or set by user, so i am not sure that such intermediate temporaries in PrintSelf() are really a big help, specially filled with zeros.

@jhlegarreta
Copy link
Copy Markdown
Member Author

Thanks again @issakomi 💯.

setting this in constructor (also removed {} and : m_MaxDistance(1)) removes all Valgrind errors.

Nice. Not sure why other member variables of the same type and initialized with the empty braces are not throwing Valgrind defects, though 😔.

Edit: only exactly like above, if you let : m_MaxDistance(1) and initialize only m_PixelCount - errors are still here. Ohh.

Worrying if they refer to m_PixelCount.

Edit:
Sorry, i don't think PrintSelf() it is a kind of in deep debug, IMHO it is just to see parameters, defaults or set by user, so i am not sure that such intermediate temporaries in PrintSelf() are really a big help, specially filled with zeros. I would prefer to remove internals like these two arrays from PrintSelf() and look closer first. But you decide.

The ITK SW Guides states that all member variables should be printed whether they are publicly exposed or not (section C.21). I see your point; maybe others have better arguments than me to illustrate the need.

@issakomi
Copy link
Copy Markdown
Member

issakomi commented Mar 1, 2021

The ITK SW Guides states that all member variables should be printed whether they are publicly exposed or not (section C.21).

OK, i see.

@issakomi
Copy link
Copy Markdown
Member

issakomi commented Mar 2, 2021

Wait... looks like it is possible to avoid Valgring detect only if remove : m_MaxDistance(1) here in .hxx

template <typename TInputImage1, typename TInputImage2>
DirectedHausdorffDistanceImageFilter<TInputImage1, TInputImage2>::DirectedHausdorffDistanceImageFilter()
  : m_MaxDistance(1)

MaxDistance: [6.93676334907487e-310]

That is what causing Valgrind error, at least in my tests, nothing else in .h, with {} or without. Only this one change seems to be enough. Current code doesn't remove Valgrind error in DirectedHausdorffDistanceImageFilter

Edit: IMHO worth to try, size 1 is anyway overridden later in BeforeThreadedGenerateData()

Initialize member variables.

Fixes:
```
UMC ==4407== Conditional jump or move depends on uninitialised value(s)
==4407==    at 0xC3A9316:
double_conversion::DoubleToStringConverter::ToShortestIeeeNumber(double,
double_conversion::StringBuilder*,
double_conversion::DoubleToStringConverter::DtoaMode) const
(double-to-string.cc:172)
==4407==    by 0x6835CE6:
double_conversion::DoubleToStringConverter::ToShortest(double,
double_conversion::StringBuilder*) const (double-to-string.h:166)
==4407==    by 0x68354F6: (anonymous
namespace)::ConvertToShortest(double_conversion::DoubleToStringConverter
const&, double, double_conversion::StringBuilder&)
(itkNumberToString.cxx:31)
==4407==    by 0x68355F8: std::string (anonymous
namespace)::FloatingPointNumberToString(double) (itkNumberToString.cxx:55)
==4407==    by 0x683555B: itk::NumberToString::operator()(double) const
(itkNumberToString.cxx:71)
==4407==    by 0x6834EC5: std::ostream& itk::operator<< (std::ostream&,
itk::Array const&) (itkArrayOutputSpecialization.cxx:37)
==4407==    by 0x250C46: itk::DirectedHausdorffDistanceImageFilter,
itk::Image >::PrintSelf(std::ostream&, itk::Indent) const
(itkDirectedHausdorffDistanceImageFilter.hxx:222)
==4407==    by 0x67FBB8D: itk::LightObject::Print(std::ostream&,
itk::Indent) const (itkLightObject.cxx:125)
==4407==    by 0x24E25F: itkDirectedHausdorffDistanceImageFilterTest(int,
char**) (itkDirectedHausdorffDistanceImageFilterTest.cxx:60)
==4407==    by 0x1D634B: main (ITKDistanceMapTestDriver.cxx:217)
```

and
```
UMC ==5645== Conditional jump or move depends on uninitialised value(s)
==5645==    at 0x927FDB0: std::ostreambuf_iterator > std::num_put >
>::_M_insert_int(std::ostreambuf_iterator >, std::ios_base&, char,
>unsigned long) const (in /usr/lib64/libstdc++.so.6.0.19)
==5645==    by 0x928004C: std::num_put >
>::do_put(std::ostreambuf_iterator >, std::ios_base&, char, unsigned long)
>const (in /usr/lib64/libstdc++.so.6.0.19)
==5645==    by 0x928C2ED: std::ostream& std::ostream::_M_insert(unsigned
long) (in /usr/lib64/libstdc++.so.6.0.19)
==5645==    by 0x24F2B6: std::ostream& itk::operator<< <2u>(std::ostream&,
itk::Size<2u> const&) (itkSize.h:412)
==5645==    by 0x2C6068: itk::ImagePCAShapeModelEstimator, itk::Image
>::PrintSelf(std::ostream&, itk::Indent) const
>(itkImagePCAShapeModelEstimator.hxx:52)
==5645==    by 0x6DBFB8D: itk::LightObject::Print(std::ostream&,
itk::Indent) const (itkLightObject.cxx:125)
==5645==    by 0x2C2F09: itkImagePCAShapeModelEstimatorTest(int, char**)
(itkImagePCAShapeModelEstimatorTest.cxx:141)
==5645==    by 0x20DA6B: main (ITKImageStatisticsTestDriver.cxx:272)
```

and
```
UMC ==6426== Conditional jump or move depends on uninitialised value(s)
==6426==    at 0x10E3942C: __printf_fp_l (in /usr/lib64/libc-2.17.so)
==6426==    by 0x10E38526: vfprintf (in /usr/lib64/libc-2.17.so)
==6426==    by 0x10E63178: vsnprintf (in /usr/lib64/libc-2.17.so)
==6426==    by 0x1095263C: ??? (in /usr/lib64/libstdc++.so.6.0.19)
==6426==    by 0x10958CA3: std::ostreambuf_iterator > std::num_put >
>::_M_insert_float(std::ostreambuf_iterator >, std::ios_base&, char, char,
>double) const (in /usr/lib64/libstdc++.so.6.0.19)
==6426==    by 0x10958F8F: std::num_put >
>::do_put(std::ostreambuf_iterator >, std::ios_base&, char, double) const
>(in /usr/lib64/libstdc++.so.6.0.19)
==6426==    by 0x10964B94: std::ostream& std::ostream::_M_insert(double)
(in /usr/lib64/libstdc++.so.6.0.19)
==6426==    by 0x738573: itk::MultiphaseSparseFiniteDifferenceImageFilter,
itk::Image, itk::Image, itk::ScalarChanAndVeseLevelSetFunction,
itk::Image, itk::ConstrainedRegionBasedLevelSetFunctionSharedData,
itk::Image, itk::ScalarChanAndVeseLevelSetFunctionData, itk::Image > > >,
unsigned int>::PrintSelf(std::ostream&, itk::Indent) const
(itkMultiphaseSparseFiniteDifferenceImageFilter.hxx:1421)
==6426==    by 0xE497B8D: itk::LightObject::Print(std::ostream&,
itk::Indent) const (itkLightObject.cxx:125)
==6426==    by 0x736F6D:
itkScalarChanAndVeseSparseLevelSetImageFilterTest1(int, char**)
(itkScalarChanAndVeseSparseLevelSetImageFilterTest1.cxx:54)
==6426==    by 0x331ADE: main (ITKReviewTestDriver.cxx:327)

UMC ==6426== Conditional jump or move depends on uninitialised value(s)
==6426==    at 0x10957DB0: std::ostreambuf_iterator > std::num_put >
>::_M_insert_int(std::ostreambuf_iterator >, std::ios_base&, char,
>unsigned long) const (in /usr/lib64/libstdc++.so.6.0.19)
==6426==    by 0x1095804C: std::num_put >
>::do_put(std::ostreambuf_iterator >, std::ios_base&, char, unsigned long)
>const (in /usr/lib64/libstdc++.so.6.0.19)
==6426==    by 0x109642ED: std::ostream& std::ostream::_M_insert(unsigned
long) (in /usr/lib64/libstdc++.so.6.0.19)
==6426==    by 0x7385BB: itk::MultiphaseSparseFiniteDifferenceImageFilter,
itk::Image, itk::Image, itk::ScalarChanAndVeseLevelSetFunction,
itk::Image, itk::ConstrainedRegionBasedLevelSetFunctionSharedData,
itk::Image, itk::ScalarChanAndVeseLevelSetFunctionData, itk::Image > > >,
unsigned int>::PrintSelf(std::ostream&, itk::Indent) const
(itkMultiphaseSparseFiniteDifferenceImageFilter.hxx:1422)
==6426==    by 0xE497B8D: itk::LightObject::Print(std::ostream&,
itk::Indent) const (itkLightObject.cxx:125)
==6426==    by 0x736F6D:
itkScalarChanAndVeseSparseLevelSetImageFilterTest1(int, char**)
(itkScalarChanAndVeseSparseLevelSetImageFilterTest1.cxx:54)
==6426==    by 0x331ADE: main (ITKReviewTestDriver.cxx:327
```

signaled at:
https://open.cdash.org/viewDynamicAnalysis.php?buildid=7070588
@jhlegarreta jhlegarreta force-pushed the FixUninitializedVariables branch from 0fa8eba to f60a74e Compare March 2, 2021 01:09
@jhlegarreta
Copy link
Copy Markdown
Member Author

jhlegarreta commented Mar 2, 2021

looks like it is possible to avoid Valgring detect only if remove : m_MaxDistance(1) here in .hxx
Edit: IMHO worth to try, size 1 is anyway overridden later in BeforeThreadedGenerateData()

Makes sense now ! Double checked the faulty line in the Valgrind message, and it was pointing to m_MaxDistance; so looks like my attempts at identifying the faulty variable were off by one line 😓 .

So, @issakomi 🏅 for the relentless effort made on this.

Force pushed a commit removing the single element allocation call from the implementation file; in my mind makes more sense to have these variable not to have any elements when instantiating this class.

Thanks to all for the patience and for bearing with the process.

@blowekamp
Copy link
Copy Markdown
Member

Great work tracking this down!

I saw some arrays of number of threads. I glances at the code, and each thread is accessing it's id's index concurrently. This causes false sharing of memory between threads and can be a significant performance hinderance. Additionally, it restricts the algorithm to use the older fixed threading model. It looks like this filter could use some updating and improvements.

@jhlegarreta
Copy link
Copy Markdown
Member Author

jhlegarreta commented Mar 2, 2021

I saw some arrays of number of threads. I glances at the code, and each thread is accessing it's id's index concurrently. This causes false sharing of memory between threads and can be a significant performance hinderance. Additionally, it restricts the algorithm to use the older fixed threading model. It looks like this filter could use some updating and improvements.

Interesting. Thanks for casting light to this Brad ! Looks like that would deserve opening an issue to keep track of it?

@thewtex
Copy link
Copy Markdown
Member

thewtex commented Mar 4, 2021

Merging for ITK 5.2rc03.

@thewtex thewtex merged commit 220b93a into InsightSoftwareConsortium:master Mar 4, 2021
@jhlegarreta jhlegarreta deleted the FixUninitializedVariables branch March 4, 2021 14:24
hjmjohnson pushed a commit to hjmjohnson/ITK that referenced this pull request May 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Filtering Issues affecting the Filtering module type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants